diff options
337 files changed, 25901 insertions, 2355 deletions
diff --git a/.gitignore b/.gitignore index 8e1b73f622..5584b5fcb0 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,7 @@ qemu-ga qemu-bridge-helper qemu-monitor.texi vscclient -QMP/qmp-commands.txt +qmp-commands.txt test-bitops test-coroutine test-int128 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..90f167630a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,71 @@ +language: c +python: + - "2.4" +compiler: + - gcc + - clang +env: + global: + - TEST_CMD="make check" + - EXTRA_CONFIG="" + # Development packages, EXTRA_PKGS saved for additional builds + - CORE_PKGS="libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev" + - NET_PKGS="libseccomp-dev libgnutls-dev libssh2-1-dev libspice-server-dev libspice-protocol-dev libnss3-dev" + - GUI_PKGS="libgtk-3-dev libvte-2.90-dev libsdl1.2-dev libpng12-dev libpixman-1-dev" + - EXTRA_PKGS="" + matrix: + - TARGETS=alpha-softmmu,alpha-linux-user + - TARGETS=arm-softmmu,arm-linux-user + - TARGETS=cris-softmmu + - TARGETS=i386-softmmu,x86_64-softmmu + - TARGETS=lm32-softmmu + - TARGETS=m68k-softmmu + - TARGETS=microblaze-softmmu,microblazeel-softmmu + - TARGETS=mips-softmmu,mips64-softmmu,mips64el-softmmu,mipsel-softmmu + - TARGETS=moxie-softmmu + - TARGETS=or32-softmmu, + - TARGETS=ppc-softmmu,ppc64-softmmu,ppcemb-softmmu + - TARGETS=s390x-softmmu + - TARGETS=sh4-softmmu,sh4eb-softmmu + - TARGETS=sparc-softmmu,sparc64-softmmu + - TARGETS=unicore32-softmmu + - TARGETS=xtensa-softmmu,xtensaeb-softmmu +before_install: + - git submodule update --init --recursive + - sudo apt-get update -qq + - sudo apt-get install -qq ${CORE_PKGS} ${NET_PKGS} ${GUI_PKGS} ${EXTRA_PKGS} +script: "./configure --target-list=${TARGETS} ${EXTRA_CONFIG} && make && ${TEST_CMD}" +matrix: + # We manually include a number of additional build for non-standard bits + include: + # Debug related options + - env: TARGETS=i386-softmmu,x86_64-softmmu + EXTRA_CONFIG="--enable-debug" + compiler: gcc + - env: TARGETS=i386-softmmu,x86_64-softmmu + EXTRA_CONFIG="--enable-debug --enable-tcg-interpreter" + compiler: gcc + # Currently configure doesn't force --disable-pie + - env: TARGETS=i386-softmmu,x86_64-softmmu + EXTRA_CONFIG="--enable-gprof --enable-gcov --disable-pie" + compiler: gcc + - env: TARGETS=i386-softmmu,x86_64-softmmu + EXTRA_PKGS="sparse" + EXTRA_CONFIG="--enable-sparse" + compiler: gcc + # All the trace backends (apart from dtrace) + - env: TARGETS=i386-softmmu,x86_64-softmmu + EXTRA_CONFIG="--enable-trace-backend=stderr" + compiler: gcc + - env: TARGETS=i386-softmmu,x86_64-softmmu + EXTRA_CONFIG="--enable-trace-backend=simple" + compiler: gcc + - env: TARGETS=i386-softmmu,x86_64-softmmu + EXTRA_CONFIG="--enable-trace-backend=ftrace" + TEST_CMD="" + compiler: gcc + # This disabled make check for the ftrace backend which needs more setting up + # Currently broken on 12.04 due to mis-packaged liburcu and changed API, will be pulled. + #- env: TARGETS=i386-softmmu,x86_64-softmmu + # EXTRA_PKGS="liblttng-ust-dev liburcu-dev" + # EXTRA_CONFIG="--enable-trace-backend=ust" diff --git a/MAINTAINERS b/MAINTAINERS index 77edacf271..c19133f1a3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -639,6 +639,8 @@ S: Supported F: block* F: block/ F: hw/block/ +T: git git://repo.or.cz/qemu/kevin.git block +T: git git://github.com/stefanha/qemu.git block Character Devices M: Anthony Liguori <aliguori@amazon.com> @@ -699,6 +701,7 @@ S: Supported F: monitor.c F: hmp.c F: hmp-commands.hx +T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp Network device layer M: Anthony Liguori <aliguori@amazon.com> @@ -720,6 +723,7 @@ M: Luiz Capitulino <lcapitulino@redhat.com> M: Michael Roth <mdroth@linux.vnet.ibm.com> S: Supported F: qapi/ +T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp QAPI Schema M: Eric Blake <eblake@redhat.com> @@ -727,6 +731,7 @@ M: Luiz Capitulino <lcapitulino@redhat.com> M: Markus Armbruster <armbru@redhat.com> S: Supported F: qapi-schema.json +T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp QMP M: Luiz Capitulino <lcapitulino@redhat.com> @@ -735,6 +740,7 @@ F: qmp.c F: monitor.c F: qmp-commands.hx F: QMP/ +T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp SLIRP M: Jan Kiszka <jan.kiszka@siemens.com> @@ -857,3 +863,43 @@ Stable 0.10 L: qemu-stable@nongnu.org T: git git://git.qemu-project.org/qemu-stable-0.10.git S: Orphan + +Block drivers +------------- +VMDK +M: Fam Zheng <famz@redhat.com> +S: Supported +F: block/vmdk.c + +RBD +M: Josh Durgin <josh.durgin@inktank.com> +S: Supported +F: block/rbd.c + +Sheepdog +M: MORITA Kazutaka <morita.kazutaka@lab.ntt.co.jp> +M: Liu Yuan <namei.unix@gmail.com> +S: Supported +F: block/sheepdog.c + +VHDX +M: Jeff Cody <jcody@redhat.com> +S: Supported +F: block/vhdx* + +VDI +M: Stefan Weil <sw@weilnetz.de> +S: Maintained +F: block/vdi.c + +iSCSI +M: Ronnie Sahlberg <ronniesahlberg@gmail.com> +M: Paolo Bonzini <pbonzini@redhat.com> +M: Peter Lieven <pl@kamp.de> +S: Supported +F: block/iscsi.c + +SSH +M: Richard W.M. Jones <rjones@redhat.com> +S: Supported +F: block/ssh.c diff --git a/Makefile b/Makefile index b15003f9d2..3321b98167 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,14 @@ CONFIG_ALL=y include $(SRC_PATH)/rules.mak config-host.mak: $(SRC_PATH)/configure @echo $@ is out-of-date, running configure - @sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh + @# TODO: The next lines include code which supports a smooth + @# transition from old configurations without config.status. + @# This code can be removed after QEMU 1.7. + @if test -x config.status; then \ + ./config.status; \ + else \ + sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh; \ + fi else config-host.mak: ifneq ($(filter-out %clean,$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail)) @@ -280,7 +287,7 @@ distclean: clean KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \ ar de en-us fi fr-be hr it lv nl pl ru th \ common de-ch es fo fr-ca hu ja mk nl-be pt sl tr \ -bepo +bepo cz ifdef INSTALL_BLOBS BLOBS=bios.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \ diff --git a/VERSION b/VERSION index 0a8112bd21..86e63cc647 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6.50 +1.6.90 diff --git a/arch_init.c b/arch_init.c index 7545d96739..e0acbc5661 100644 --- a/arch_init.c +++ b/arch_init.c @@ -850,14 +850,6 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) { if (ch != 0 || !is_zero_range(host, size)) { memset(host, ch, size); -#ifndef _WIN32 - if (ch == 0 && (!kvm_enabled() || kvm_has_sync_mmu())) { - size = size & ~(getpagesize() - 1); - if (size > 0) { - qemu_madvise(host, size, QEMU_MADV_DONTNEED); - } - } -#endif } } diff --git a/audio/audio.c b/audio/audio.c index af4cdf60e7..b3db67979d 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1124,7 +1124,8 @@ static int audio_is_timer_needed (void) static void audio_reset_timer (AudioState *s) { if (audio_is_timer_needed ()) { - timer_mod (s->ts, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 1); + timer_mod (s->ts, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); } else { timer_del (s->ts); diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h index 30849a62a1..77cc89b9e8 100644 --- a/audio/mixeng_template.h +++ b/audio/mixeng_template.h @@ -35,7 +35,7 @@ #define IN_T glue (glue (ITYPE, BSIZE), _t) #ifdef FLOAT_MIXENG -static mixeng_real inline glue (conv_, ET) (IN_T v) +static inline mixeng_real glue (conv_, ET) (IN_T v) { IN_T nv = ENDIAN_CONVERT (v); @@ -54,7 +54,7 @@ static mixeng_real inline glue (conv_, ET) (IN_T v) #endif } -static IN_T inline glue (clip_, ET) (mixeng_real v) +static inline IN_T glue (clip_, ET) (mixeng_real v) { if (v >= 0.5) { return IN_MAX; diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 007c64115a..5a73716032 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -849,6 +849,10 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) static void *oss_audio_init (void) { + if (access(conf.devpath_in, R_OK | W_OK) < 0 || + access(conf.devpath_out, R_OK | W_OK) < 0) { + return NULL; + } return &conf; } diff --git a/block.c b/block.c index fd05a8008a..382ea71f4b 100644 --- a/block.c +++ b/block.c @@ -640,7 +640,7 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) if (length < 0) { return length; } - hint = length >> BDRV_SECTOR_BITS; + hint = DIV_ROUND_UP(length, BDRV_SECTOR_SIZE); } bs->total_sectors = hint; @@ -999,20 +999,23 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) } /* backing files always opened read-only */ - back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT); + back_flags = bs->open_flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | + BDRV_O_COPY_ON_READ); ret = bdrv_open(bs->backing_hd, *backing_filename ? backing_filename : NULL, options, back_flags, back_drv, &local_err); - pstrcpy(bs->backing_file, sizeof(bs->backing_file), - bs->backing_hd->file->filename); if (ret < 0) { bdrv_unref(bs->backing_hd); bs->backing_hd = NULL; bs->open_flags |= BDRV_O_NO_BACKING; - error_propagate(errp, local_err); + error_setg(errp, "Could not open backing file: %s", + error_get_pretty(local_err)); + error_free(local_err); return ret; } + pstrcpy(bs->backing_file, sizeof(bs->backing_file), + bs->backing_hd->file->filename); return 0; } @@ -1083,8 +1086,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, snprintf(backing_filename, sizeof(backing_filename), "%s", filename); } else if (!realpath(filename, backing_filename)) { - error_setg_errno(errp, errno, "Could not resolve path '%s'", filename); ret = -errno; + error_setg_errno(errp, errno, "Could not resolve path '%s'", filename); goto fail; } @@ -1134,6 +1137,11 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, if (drvname) { drv = bdrv_find_format(drvname); qdict_del(options, "driver"); + if (!drv) { + error_setg(errp, "Invalid driver: '%s'", drvname); + ret = -EINVAL; + goto unlink_and_fail; + } } if (!drv) { @@ -2868,9 +2876,10 @@ int64_t bdrv_getlength(BlockDriverState *bs) if (!drv) return -ENOMEDIUM; - if (bdrv_dev_has_removable_media(bs)) { - if (drv->bdrv_getlength) { - return drv->bdrv_getlength(bs); + if (drv->has_variable_length) { + int ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + return ret; } } return bs->total_sectors * BDRV_SECTOR_SIZE; diff --git a/block/Makefile.objs b/block/Makefile.objs index 3bb85b535c..f43ecbc044 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -2,7 +2,7 @@ block-obj-y += raw_bsd.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o v block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed-check.o -block-obj-y += vhdx.o +block-obj-$(CONFIG_VHDX) += vhdx.o vhdx-endian.o vhdx-log.o block-obj-y += parallels.o blkdebug.o blkverify.o block-obj-y += snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o diff --git a/block/iscsi.c b/block/iscsi.c index a2a961e163..a2d578c0a7 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -866,7 +866,7 @@ retry: /* in case the get_lba_status_callout fails (i.e. * because the device is busy or the cmd is not * supported) we pretend all blocks are allocated - * for backwards compatiblity */ + * for backwards compatibility */ goto out; } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 0348b971b1..791083a0ef 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -101,7 +101,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, /* set new table */ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); cpu_to_be32w((uint32_t*)data, new_l1_size); - cpu_to_be64wu((uint64_t*)(data + 4), new_l1_table_offset); + stq_be_p(data + 4, new_l1_table_offset); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data)); if (ret < 0) { goto fail; @@ -290,7 +290,7 @@ static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, uint64_t *l2_table, uint64_t stop_flags) { int i; - uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW2_CLUSTER_COMPRESSED; + uint64_t mask = stop_flags | L2E_OFFSET_MASK | QCOW_OFLAG_COMPRESSED; uint64_t first_entry = be64_to_cpu(l2_table[0]); uint64_t offset = first_entry & mask; diff --git a/block/qcow2.c b/block/qcow2.c index c1abaffa19..6e5d98dc48 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1584,6 +1584,16 @@ static int qcow2_create2(const char *filename, int64_t total_size, } } + bdrv_close(bs); + + /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */ + ret = bdrv_open(bs, filename, NULL, + BDRV_O_RDWR | BDRV_O_CACHE_WB, drv, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + goto out; + } + ret = 0; out: bdrv_unref(bs); @@ -1939,13 +1949,22 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) { BDRVQcowState *s = bs->opaque; + int64_t total_sectors = bs->total_sectors; int growable = bs->growable; + bool zero_beyond_eof = bs->zero_beyond_eof; int ret; BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); bs->growable = 1; + bs->zero_beyond_eof = false; ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov); bs->growable = growable; + bs->zero_beyond_eof = zero_beyond_eof; + + /* bdrv_co_do_writev will have increased the total_sectors value to include + * the VM state - the VM state is however not an actual part of the block + * device, therefore, we need to restore the old value. */ + bs->total_sectors = total_sectors; return ret; } diff --git a/block/raw-posix.c b/block/raw-posix.c index 6f03fbf793..f836c8e745 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -310,7 +310,6 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, if (ret == -EROFS) { ret = -EACCES; } - error_setg_errno(errp, -ret, "Could not open file"); goto fail; } s->fd = fd; @@ -1715,7 +1714,8 @@ static BlockDriver bdrv_host_floppy = { .bdrv_aio_flush = raw_aio_flush, .bdrv_truncate = raw_truncate, - .bdrv_getlength = raw_getlength, + .bdrv_getlength = raw_getlength, + .has_variable_length = true, .bdrv_get_allocated_file_size = raw_get_allocated_file_size, @@ -1824,7 +1824,8 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_aio_flush = raw_aio_flush, .bdrv_truncate = raw_truncate, - .bdrv_getlength = raw_getlength, + .bdrv_getlength = raw_getlength, + .has_variable_length = true, .bdrv_get_allocated_file_size = raw_get_allocated_file_size, @@ -1840,7 +1841,8 @@ static BlockDriver bdrv_host_cdrom = { #endif /* __linux__ */ #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) -static int cdrom_open(BlockDriverState *bs, QDict *options, int flags) +static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { BDRVRawState *s = bs->opaque; Error *local_err = NULL; @@ -1951,7 +1953,8 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_aio_flush = raw_aio_flush, .bdrv_truncate = raw_truncate, - .bdrv_getlength = raw_getlength, + .bdrv_getlength = raw_getlength, + .has_variable_length = true, .bdrv_get_allocated_file_size = raw_get_allocated_file_size, diff --git a/block/raw-win32.c b/block/raw-win32.c index 676b5701db..2bad5a39b4 100644 --- a/block/raw-win32.c +++ b/block/raw-win32.c @@ -280,7 +280,6 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, } else { ret = -EINVAL; } - error_setg_errno(errp, -ret, "Could not open file"); goto fail; } @@ -616,7 +615,9 @@ static BlockDriver bdrv_host_device = { .bdrv_aio_writev = raw_aio_writev, .bdrv_aio_flush = raw_aio_flush, - .bdrv_getlength = raw_getlength, + .bdrv_getlength = raw_getlength, + .has_variable_length = true, + .bdrv_get_allocated_file_size = raw_get_allocated_file_size, }; diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 0078c1baeb..2265dcc03f 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -178,6 +178,7 @@ static BlockDriver bdrv_raw = { .bdrv_co_get_block_status = &raw_co_get_block_status, .bdrv_truncate = &raw_truncate, .bdrv_getlength = &raw_getlength, + .has_variable_length = true, .bdrv_get_info = &raw_get_info, .bdrv_is_inserted = &raw_is_inserted, .bdrv_media_changed = &raw_media_changed, diff --git a/block/sheepdog.c b/block/sheepdog.c index 5f81c93ee3..ef387de71f 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -125,8 +125,9 @@ typedef struct SheepdogObjReq { uint32_t data_length; uint64_t oid; uint64_t cow_oid; - uint32_t copies; - uint32_t rsvd; + uint8_t copies; + uint8_t copy_policy; + uint8_t reserved[6]; uint64_t offset; } SheepdogObjReq; @@ -138,7 +139,9 @@ typedef struct SheepdogObjRsp { uint32_t id; uint32_t data_length; uint32_t result; - uint32_t copies; + uint8_t copies; + uint8_t copy_policy; + uint8_t reserved[2]; uint32_t pad[6]; } SheepdogObjRsp; @@ -151,7 +154,9 @@ typedef struct SheepdogVdiReq { uint32_t data_length; uint64_t vdi_size; uint32_t vdi_id; - uint32_t copies; + uint8_t copies; + uint8_t copy_policy; + uint8_t reserved[2]; uint32_t snapid; uint32_t pad[3]; } SheepdogVdiReq; @@ -222,6 +227,11 @@ static inline uint64_t data_oid_to_idx(uint64_t oid) return oid & (MAX_DATA_OBJS - 1); } +static inline uint32_t oid_to_vid(uint64_t oid) +{ + return (oid & ~VDI_BIT) >> VDI_SPACE_SHIFT; +} + static inline uint64_t vid_to_vdi_oid(uint32_t vid) { return VDI_BIT | ((uint64_t)vid << VDI_SPACE_SHIFT); @@ -289,11 +299,14 @@ struct SheepdogAIOCB { Coroutine *coroutine; void (*aio_done_func)(SheepdogAIOCB *); - bool canceled; + bool cancelable; + bool *finished; int nr_pending; }; typedef struct BDRVSheepdogState { + BlockDriverState *bs; + SheepdogInode inode; uint32_t min_dirty_data_idx; @@ -313,8 +326,11 @@ typedef struct BDRVSheepdogState { Coroutine *co_recv; uint32_t aioreq_seq_num; + + /* Every aio request must be linked to either of these queues. */ QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head; QLIST_HEAD(pending_aio_head, AIOReq) pending_aio_head; + QLIST_HEAD(failed_aio_head, AIOReq) failed_aio_head; } BDRVSheepdogState; static const char * sd_strerror(int err) @@ -403,6 +419,7 @@ static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req) { SheepdogAIOCB *acb = aio_req->aiocb; + acb->cancelable = false; QLIST_REMOVE(aio_req, aio_siblings); g_free(aio_req); @@ -411,23 +428,68 @@ static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req) static void coroutine_fn sd_finish_aiocb(SheepdogAIOCB *acb) { - if (!acb->canceled) { - qemu_coroutine_enter(acb->coroutine, NULL); + qemu_coroutine_enter(acb->coroutine, NULL); + if (acb->finished) { + *acb->finished = true; } qemu_aio_release(acb); } +/* + * Check whether the specified acb can be canceled + * + * We can cancel aio when any request belonging to the acb is: + * - Not processed by the sheepdog server. + * - Not linked to the inflight queue. + */ +static bool sd_acb_cancelable(const SheepdogAIOCB *acb) +{ + BDRVSheepdogState *s = acb->common.bs->opaque; + AIOReq *aioreq; + + if (!acb->cancelable) { + return false; + } + + QLIST_FOREACH(aioreq, &s->inflight_aio_head, aio_siblings) { + if (aioreq->aiocb == acb) { + return false; + } + } + + return true; +} + static void sd_aio_cancel(BlockDriverAIOCB *blockacb) { SheepdogAIOCB *acb = (SheepdogAIOCB *)blockacb; + BDRVSheepdogState *s = acb->common.bs->opaque; + AIOReq *aioreq, *next; + bool finished = false; + + acb->finished = &finished; + while (!finished) { + if (sd_acb_cancelable(acb)) { + /* Remove outstanding requests from pending and failed queues. */ + QLIST_FOREACH_SAFE(aioreq, &s->pending_aio_head, aio_siblings, + next) { + if (aioreq->aiocb == acb) { + free_aio_req(s, aioreq); + } + } + QLIST_FOREACH_SAFE(aioreq, &s->failed_aio_head, aio_siblings, + next) { + if (aioreq->aiocb == acb) { + free_aio_req(s, aioreq); + } + } - /* - * Sheepdog cannot cancel the requests which are already sent to - * the servers, so we just complete the request with -EIO here. - */ - acb->ret = -EIO; - qemu_coroutine_enter(acb->coroutine, NULL); - acb->canceled = true; + assert(acb->nr_pending == 0); + sd_finish_aiocb(acb); + return; + } + qemu_aio_wait(); + } } static const AIOCBInfo sd_aiocb_info = { @@ -448,7 +510,8 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, acb->nb_sectors = nb_sectors; acb->aio_done_func = NULL; - acb->canceled = false; + acb->cancelable = true; + acb->finished = NULL; acb->coroutine = qemu_coroutine_self(); acb->ret = 0; acb->nr_pending = 0; @@ -489,13 +552,13 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, int ret; ret = qemu_co_send(sockfd, hdr, sizeof(*hdr)); - if (ret < sizeof(*hdr)) { + if (ret != sizeof(*hdr)) { error_report("failed to send a req, %s", strerror(errno)); return ret; } ret = qemu_co_send(sockfd, data, *wlen); - if (ret < *wlen) { + if (ret != *wlen) { error_report("failed to send a req, %s", strerror(errno)); } @@ -541,7 +604,7 @@ static coroutine_fn void do_co_req(void *opaque) qemu_aio_set_fd_handler(sockfd, restart_co_req, NULL, co); ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr)); - if (ret < sizeof(*hdr)) { + if (ret != sizeof(*hdr)) { error_report("failed to get a rsp, %s", strerror(errno)); ret = -errno; goto out; @@ -553,7 +616,7 @@ static coroutine_fn void do_co_req(void *opaque) if (*rlen) { ret = qemu_co_recv(sockfd, data, *rlen); - if (ret < *rlen) { + if (ret != *rlen) { error_report("failed to get the data, %s", strerror(errno)); ret = -errno; goto out; @@ -596,11 +659,13 @@ static int do_req(int sockfd, SheepdogReq *hdr, void *data, return srco.ret; } -static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, +static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, struct iovec *iov, int niov, bool create, enum AIOCBState aiocb_type); -static int coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req); - +static void coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req); +static int reload_inode(BDRVSheepdogState *s, uint32_t snapid, const char *tag); +static int get_sheep_fd(BDRVSheepdogState *s); +static void co_write_request(void *opaque); static AIOReq *find_pending_req(BDRVSheepdogState *s, uint64_t oid) { @@ -623,22 +688,59 @@ static void coroutine_fn send_pending_req(BDRVSheepdogState *s, uint64_t oid) { AIOReq *aio_req; SheepdogAIOCB *acb; - int ret; while ((aio_req = find_pending_req(s, oid)) != NULL) { acb = aio_req->aiocb; /* move aio_req from pending list to inflight one */ QLIST_REMOVE(aio_req, aio_siblings); QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); - ret = add_aio_request(s, aio_req, acb->qiov->iov, - acb->qiov->niov, false, acb->aiocb_type); - if (ret < 0) { - error_report("add_aio_request is failed"); - free_aio_req(s, aio_req); - if (!acb->nr_pending) { - sd_finish_aiocb(acb); - } + add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, false, + acb->aiocb_type); + } +} + +static coroutine_fn void reconnect_to_sdog(void *opaque) +{ + BDRVSheepdogState *s = opaque; + AIOReq *aio_req, *next; + + qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL); + close(s->fd); + s->fd = -1; + + /* Wait for outstanding write requests to be completed. */ + while (s->co_send != NULL) { + co_write_request(opaque); + } + + /* Try to reconnect the sheepdog server every one second. */ + while (s->fd < 0) { + s->fd = get_sheep_fd(s); + if (s->fd < 0) { + DPRINTF("Wait for connection to be established\n"); + co_aio_sleep_ns(bdrv_get_aio_context(s->bs), QEMU_CLOCK_REALTIME, + 1000000000ULL); } + }; + + /* + * Now we have to resend all the request in the inflight queue. However, + * resend_aioreq() can yield and newly created requests can be added to the + * inflight queue before the coroutine is resumed. To avoid mixing them, we + * have to move all the inflight requests to the failed queue before + * resend_aioreq() is called. + */ + QLIST_FOREACH_SAFE(aio_req, &s->inflight_aio_head, aio_siblings, next) { + QLIST_REMOVE(aio_req, aio_siblings); + QLIST_INSERT_HEAD(&s->failed_aio_head, aio_req, aio_siblings); + } + + /* Resend all the failed aio requests. */ + while (!QLIST_EMPTY(&s->failed_aio_head)) { + aio_req = QLIST_FIRST(&s->failed_aio_head); + QLIST_REMOVE(aio_req, aio_siblings); + QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); + resend_aioreq(s, aio_req); } } @@ -658,15 +760,11 @@ static void coroutine_fn aio_read_response(void *opaque) SheepdogAIOCB *acb; uint64_t idx; - if (QLIST_EMPTY(&s->inflight_aio_head)) { - goto out; - } - /* read a header */ ret = qemu_co_recv(fd, &rsp, sizeof(rsp)); - if (ret < 0) { + if (ret != sizeof(rsp)) { error_report("failed to get the header, %s", strerror(errno)); - goto out; + goto err; } /* find the right aio_req from the inflight aio list */ @@ -677,7 +775,7 @@ static void coroutine_fn aio_read_response(void *opaque) } if (!aio_req) { error_report("cannot find aio_req %x", rsp.id); - goto out; + goto err; } acb = aio_req->aiocb; @@ -715,9 +813,9 @@ static void coroutine_fn aio_read_response(void *opaque) case AIOCB_READ_UDATA: ret = qemu_co_recvv(fd, acb->qiov->iov, acb->qiov->niov, aio_req->iov_offset, rsp.data_length); - if (ret < 0) { + if (ret != rsp.data_length) { error_report("failed to get the data, %s", strerror(errno)); - goto out; + goto err; } break; case AIOCB_FLUSH_CACHE: @@ -748,11 +846,20 @@ static void coroutine_fn aio_read_response(void *opaque) case SD_RES_SUCCESS: break; case SD_RES_READONLY: - ret = resend_aioreq(s, aio_req); - if (ret == SD_RES_SUCCESS) { - goto out; + if (s->inode.vdi_id == oid_to_vid(aio_req->oid)) { + ret = reload_inode(s, 0, ""); + if (ret < 0) { + goto err; + } } - /* fall through */ + if (is_data_obj(aio_req->oid)) { + aio_req->oid = vid_to_data_oid(s->inode.vdi_id, + data_oid_to_idx(aio_req->oid)); + } else { + aio_req->oid = vid_to_vdi_oid(s->inode.vdi_id); + } + resend_aioreq(s, aio_req); + goto out; default: acb->ret = -EIO; error_report("%s", sd_strerror(rsp.result)); @@ -769,6 +876,10 @@ static void coroutine_fn aio_read_response(void *opaque) } out: s->co_recv = NULL; + return; +err: + s->co_recv = NULL; + reconnect_to_sdog(opaque); } static void co_read_response(void *opaque) @@ -997,7 +1108,7 @@ out: return ret; } -static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, +static void coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, struct iovec *iov, int niov, bool create, enum AIOCBState aiocb_type) { @@ -1059,29 +1170,25 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, /* send a header */ ret = qemu_co_send(s->fd, &hdr, sizeof(hdr)); - if (ret < 0) { - qemu_co_mutex_unlock(&s->lock); + if (ret != sizeof(hdr)) { error_report("failed to send a req, %s", strerror(errno)); - return -errno; + goto out; } if (wlen) { ret = qemu_co_sendv(s->fd, iov, niov, aio_req->iov_offset, wlen); - if (ret < 0) { - qemu_co_mutex_unlock(&s->lock); + if (ret != wlen) { error_report("failed to send a data, %s", strerror(errno)); - return -errno; } } - +out: socket_set_cork(s->fd, 0); qemu_aio_set_fd_handler(s->fd, co_read_response, NULL, s); + s->co_send = NULL; qemu_co_mutex_unlock(&s->lock); - - return 0; } -static int read_write_object(int fd, char *buf, uint64_t oid, int copies, +static int read_write_object(int fd, char *buf, uint64_t oid, uint8_t copies, unsigned int datalen, uint64_t offset, bool write, bool create, uint32_t cache_flags) { @@ -1129,7 +1236,7 @@ static int read_write_object(int fd, char *buf, uint64_t oid, int copies, } } -static int read_object(int fd, char *buf, uint64_t oid, int copies, +static int read_object(int fd, char *buf, uint64_t oid, uint8_t copies, unsigned int datalen, uint64_t offset, uint32_t cache_flags) { @@ -1137,7 +1244,7 @@ static int read_object(int fd, char *buf, uint64_t oid, int copies, false, cache_flags); } -static int write_object(int fd, char *buf, uint64_t oid, int copies, +static int write_object(int fd, char *buf, uint64_t oid, uint8_t copies, unsigned int datalen, uint64_t offset, bool create, uint32_t cache_flags) { @@ -1181,51 +1288,62 @@ out: return ret; } -static int coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req) +/* Return true if the specified request is linked to the pending list. */ +static bool check_simultaneous_create(BDRVSheepdogState *s, AIOReq *aio_req) { - SheepdogAIOCB *acb = aio_req->aiocb; - bool create = false; - int ret; - - ret = reload_inode(s, 0, ""); - if (ret < 0) { - return ret; + AIOReq *areq; + QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) { + if (areq != aio_req && areq->oid == aio_req->oid) { + /* + * Sheepdog cannot handle simultaneous create requests to the same + * object, so we cannot send the request until the previous request + * finishes. + */ + DPRINTF("simultaneous create to %" PRIx64 "\n", aio_req->oid); + aio_req->flags = 0; + aio_req->base_oid = 0; + QLIST_REMOVE(aio_req, aio_siblings); + QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req, aio_siblings); + return true; + } } - aio_req->oid = vid_to_data_oid(s->inode.vdi_id, - data_oid_to_idx(aio_req->oid)); + return false; +} + +static void coroutine_fn resend_aioreq(BDRVSheepdogState *s, AIOReq *aio_req) +{ + SheepdogAIOCB *acb = aio_req->aiocb; + bool create = false; /* check whether this request becomes a CoW one */ - if (acb->aiocb_type == AIOCB_WRITE_UDATA) { + if (acb->aiocb_type == AIOCB_WRITE_UDATA && is_data_obj(aio_req->oid)) { int idx = data_oid_to_idx(aio_req->oid); - AIOReq *areq; - if (s->inode.data_vdi_id[idx] == 0) { - create = true; - goto out; - } if (is_data_obj_writable(&s->inode, idx)) { goto out; } - /* link to the pending list if there is another CoW request to - * the same object */ - QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) { - if (areq != aio_req && areq->oid == aio_req->oid) { - DPRINTF("simultaneous CoW to %" PRIx64 "\n", aio_req->oid); - QLIST_REMOVE(aio_req, aio_siblings); - QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req, aio_siblings); - return SD_RES_SUCCESS; - } + if (check_simultaneous_create(s, aio_req)) { + return; } - aio_req->base_oid = vid_to_data_oid(s->inode.data_vdi_id[idx], idx); - aio_req->flags |= SD_FLAG_CMD_COW; + if (s->inode.data_vdi_id[idx]) { + aio_req->base_oid = vid_to_data_oid(s->inode.data_vdi_id[idx], idx); + aio_req->flags |= SD_FLAG_CMD_COW; + } create = true; } out: - return add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, - create, acb->aiocb_type); + if (is_data_obj(aio_req->oid)) { + add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, create, + acb->aiocb_type); + } else { + struct iovec iov; + iov.iov_base = &s->inode; + iov.iov_len = sizeof(s->inode); + add_aio_request(s, aio_req, &iov, 1, false, AIOCB_WRITE_UDATA); + } } /* TODO Convert to fine grained options */ @@ -1255,6 +1373,8 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, Error *local_err = NULL; const char *filename; + s->bs = bs; + opts = qemu_opts_create_nofail(&runtime_opts); qemu_opts_absorb_qdict(opts, options, &local_err); if (error_is_set(&local_err)) { @@ -1268,6 +1388,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, QLIST_INIT(&s->inflight_aio_head); QLIST_INIT(&s->pending_aio_head); + QLIST_INIT(&s->failed_aio_head); s->fd = -1; memset(vdi, 0, sizeof(vdi)); @@ -1344,7 +1465,8 @@ out: } static int do_sd_create(BDRVSheepdogState *s, char *filename, int64_t vdi_size, - uint32_t base_vid, uint32_t *vdi_id, int snapshot) + uint32_t base_vid, uint32_t *vdi_id, int snapshot, + uint8_t copy_policy) { SheepdogVdiReq hdr; SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; @@ -1374,6 +1496,7 @@ static int do_sd_create(BDRVSheepdogState *s, char *filename, int64_t vdi_size, hdr.data_length = wlen; hdr.vdi_size = vdi_size; + hdr.copy_policy = copy_policy; ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen); @@ -1526,7 +1649,8 @@ static int sd_create(const char *filename, QEMUOptionParameter *options, bdrv_unref(bs); } - ret = do_sd_create(s, vdi, vdi_size, base_vid, &vid, 0); + /* TODO: allow users to specify copy number */ + ret = do_sd_create(s, vdi, vdi_size, base_vid, &vid, 0, 0); if (!prealloc || ret) { goto out; } @@ -1621,7 +1745,6 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset) */ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb) { - int ret; BDRVSheepdogState *s = acb->common.bs->opaque; struct iovec iov; AIOReq *aio_req; @@ -1643,18 +1766,13 @@ static void coroutine_fn sd_write_done(SheepdogAIOCB *acb) aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id), data_len, offset, 0, 0, offset); QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); - ret = add_aio_request(s, aio_req, &iov, 1, false, AIOCB_WRITE_UDATA); - if (ret) { - free_aio_req(s, aio_req); - acb->ret = -EIO; - goto out; - } + add_aio_request(s, aio_req, &iov, 1, false, AIOCB_WRITE_UDATA); acb->aio_done_func = sd_finish_aiocb; acb->aiocb_type = AIOCB_WRITE_UDATA; return; } -out: + sd_finish_aiocb(acb); } @@ -1716,7 +1834,7 @@ static int sd_create_branch(BDRVSheepdogState *s) */ deleted = sd_delete(s); ret = do_sd_create(s, s->name, s->inode.vdi_size, s->inode.vdi_id, &vid, - !deleted); + !deleted, s->inode.copy_policy); if (ret) { goto out; } @@ -1840,35 +1958,16 @@ static int coroutine_fn sd_co_rw_vector(void *p) } aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, old_oid, done); + QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); if (create) { - AIOReq *areq; - QLIST_FOREACH(areq, &s->inflight_aio_head, aio_siblings) { - if (areq->oid == oid) { - /* - * Sheepdog cannot handle simultaneous create - * requests to the same object. So we cannot send - * the request until the previous request - * finishes. - */ - aio_req->flags = 0; - aio_req->base_oid = 0; - QLIST_INSERT_HEAD(&s->pending_aio_head, aio_req, - aio_siblings); - goto done; - } + if (check_simultaneous_create(s, aio_req)) { + goto done; } } - QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); - ret = add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, - create, acb->aiocb_type); - if (ret < 0) { - error_report("add_aio_request is failed"); - free_aio_req(s, aio_req); - acb->ret = -EIO; - goto out; - } + add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, create, + acb->aiocb_type); done: offset = 0; idx++; @@ -1936,7 +2035,6 @@ static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs) BDRVSheepdogState *s = bs->opaque; SheepdogAIOCB *acb; AIOReq *aio_req; - int ret; if (s->cache_flags != SD_FLAG_CMD_CACHE) { return 0; @@ -1949,13 +2047,7 @@ static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs) aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id), 0, 0, 0, 0, 0); QLIST_INSERT_HEAD(&s->inflight_aio_head, aio_req, aio_siblings); - ret = add_aio_request(s, aio_req, NULL, 0, false, acb->aiocb_type); - if (ret < 0) { - error_report("add_aio_request is failed"); - free_aio_req(s, aio_req); - qemu_aio_release(acb); - return ret; - } + add_aio_request(s, aio_req, NULL, 0, false, acb->aiocb_type); qemu_coroutine_yield(); return acb->ret; @@ -2006,7 +2098,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) } ret = do_sd_create(s, s->name, s->inode.vdi_size, s->inode.vdi_id, &new_vid, - 1); + 1, s->inode.copy_policy); if (ret < 0) { error_report("failed to create inode for snapshot. %s", strerror(errno)); diff --git a/block/vhdx-endian.c b/block/vhdx-endian.c new file mode 100644 index 0000000000..fe879ed995 --- /dev/null +++ b/block/vhdx-endian.c @@ -0,0 +1,216 @@ +/* + * Block driver for Hyper-V VHDX Images + * + * Copyright (c) 2013 Red Hat, Inc., + * + * Authors: + * Jeff Cody <jcody@redhat.com> + * + * This is based on the "VHDX Format Specification v1.00", published 8/25/2012 + * by Microsoft: + * https://www.microsoft.com/en-us/download/details.aspx?id=34750 + * + * 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-common.h" +#include "block/block_int.h" +#include "block/vhdx.h" + +#include <uuid/uuid.h> + + +/* + * All the VHDX formats on disk are little endian - the following + * are helper import/export functions to correctly convert + * endianness from disk read to native cpu format, and back again. + */ + + +/* VHDX File Header */ + + +void vhdx_header_le_import(VHDXHeader *h) +{ + assert(h != NULL); + + le32_to_cpus(&h->signature); + le32_to_cpus(&h->checksum); + le64_to_cpus(&h->sequence_number); + + leguid_to_cpus(&h->file_write_guid); + leguid_to_cpus(&h->data_write_guid); + leguid_to_cpus(&h->log_guid); + + le16_to_cpus(&h->log_version); + le16_to_cpus(&h->version); + le32_to_cpus(&h->log_length); + le64_to_cpus(&h->log_offset); +} + +void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h) +{ + assert(orig_h != NULL); + assert(new_h != NULL); + + new_h->signature = cpu_to_le32(orig_h->signature); + new_h->checksum = cpu_to_le32(orig_h->checksum); + new_h->sequence_number = cpu_to_le64(orig_h->sequence_number); + + new_h->file_write_guid = orig_h->file_write_guid; + new_h->data_write_guid = orig_h->data_write_guid; + new_h->log_guid = orig_h->log_guid; + + cpu_to_leguids(&new_h->file_write_guid); + cpu_to_leguids(&new_h->data_write_guid); + cpu_to_leguids(&new_h->log_guid); + + new_h->log_version = cpu_to_le16(orig_h->log_version); + new_h->version = cpu_to_le16(orig_h->version); + new_h->log_length = cpu_to_le32(orig_h->log_length); + new_h->log_offset = cpu_to_le64(orig_h->log_offset); +} + + +/* VHDX Log Headers */ + + +void vhdx_log_desc_le_import(VHDXLogDescriptor *d) +{ + assert(d != NULL); + + le32_to_cpus(&d->signature); + le32_to_cpus(&d->trailing_bytes); + le64_to_cpus(&d->leading_bytes); + le64_to_cpus(&d->file_offset); + le64_to_cpus(&d->sequence_number); +} + +void vhdx_log_desc_le_export(VHDXLogDescriptor *d) +{ + assert(d != NULL); + + cpu_to_le32s(&d->signature); + cpu_to_le32s(&d->trailing_bytes); + cpu_to_le64s(&d->leading_bytes); + cpu_to_le64s(&d->file_offset); + cpu_to_le64s(&d->sequence_number); +} + +void vhdx_log_data_le_export(VHDXLogDataSector *d) +{ + assert(d != NULL); + + cpu_to_le32s(&d->data_signature); + cpu_to_le32s(&d->sequence_high); + cpu_to_le32s(&d->sequence_low); +} + +void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr) +{ + assert(hdr != NULL); + + le32_to_cpus(&hdr->signature); + le32_to_cpus(&hdr->checksum); + le32_to_cpus(&hdr->entry_length); + le32_to_cpus(&hdr->tail); + le64_to_cpus(&hdr->sequence_number); + le32_to_cpus(&hdr->descriptor_count); + leguid_to_cpus(&hdr->log_guid); + le64_to_cpus(&hdr->flushed_file_offset); + le64_to_cpus(&hdr->last_file_offset); +} + +void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr) +{ + assert(hdr != NULL); + + cpu_to_le32s(&hdr->signature); + cpu_to_le32s(&hdr->checksum); + cpu_to_le32s(&hdr->entry_length); + cpu_to_le32s(&hdr->tail); + cpu_to_le64s(&hdr->sequence_number); + cpu_to_le32s(&hdr->descriptor_count); + cpu_to_leguids(&hdr->log_guid); + cpu_to_le64s(&hdr->flushed_file_offset); + cpu_to_le64s(&hdr->last_file_offset); +} + + +/* Region table entries */ +void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr) +{ + assert(hdr != NULL); + + le32_to_cpus(&hdr->signature); + le32_to_cpus(&hdr->checksum); + le32_to_cpus(&hdr->entry_count); +} + +void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr) +{ + assert(hdr != NULL); + + cpu_to_le32s(&hdr->signature); + cpu_to_le32s(&hdr->checksum); + cpu_to_le32s(&hdr->entry_count); +} + +void vhdx_region_entry_le_import(VHDXRegionTableEntry *e) +{ + assert(e != NULL); + + leguid_to_cpus(&e->guid); + le64_to_cpus(&e->file_offset); + le32_to_cpus(&e->length); + le32_to_cpus(&e->data_bits); +} + +void vhdx_region_entry_le_export(VHDXRegionTableEntry *e) +{ + assert(e != NULL); + + cpu_to_leguids(&e->guid); + cpu_to_le64s(&e->file_offset); + cpu_to_le32s(&e->length); + cpu_to_le32s(&e->data_bits); +} + + +/* Metadata headers & table */ +void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr) +{ + assert(hdr != NULL); + + le64_to_cpus(&hdr->signature); + le16_to_cpus(&hdr->entry_count); +} + +void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr) +{ + assert(hdr != NULL); + + cpu_to_le64s(&hdr->signature); + cpu_to_le16s(&hdr->entry_count); +} + +void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e) +{ + assert(e != NULL); + + leguid_to_cpus(&e->item_id); + le32_to_cpus(&e->offset); + le32_to_cpus(&e->length); + le32_to_cpus(&e->data_bits); +} +void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e) +{ + assert(e != NULL); + + cpu_to_leguids(&e->item_id); + cpu_to_le32s(&e->offset); + cpu_to_le32s(&e->length); + cpu_to_le32s(&e->data_bits); +} diff --git a/block/vhdx-log.c b/block/vhdx-log.c new file mode 100644 index 0000000000..ee5583c309 --- /dev/null +++ b/block/vhdx-log.c @@ -0,0 +1,1010 @@ +/* + * Block driver for Hyper-V VHDX Images + * + * Copyright (c) 2013 Red Hat, Inc., + * + * Authors: + * Jeff Cody <jcody@redhat.com> + * + * This is based on the "VHDX Format Specification v1.00", published 8/25/2012 + * by Microsoft: + * https://www.microsoft.com/en-us/download/details.aspx?id=34750 + * + * This file covers the functionality of the metadata log writing, parsing, and + * replay. + * + * 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-common.h" +#include "block/block_int.h" +#include "qemu/module.h" +#include "block/vhdx.h" + + +typedef struct VHDXLogSequence { + bool valid; + uint32_t count; + VHDXLogEntries log; + VHDXLogEntryHeader hdr; +} VHDXLogSequence; + +typedef struct VHDXLogDescEntries { + VHDXLogEntryHeader hdr; + VHDXLogDescriptor desc[]; +} VHDXLogDescEntries; + +static const MSGUID zero_guid = { 0 }; + +/* The log located on the disk is circular buffer containing + * sectors of 4096 bytes each. + * + * It is assumed for the read/write functions below that the + * circular buffer scheme uses a 'one sector open' to indicate + * the buffer is full. Given the validation methods used for each + * sector, this method should be compatible with other methods that + * do not waste a sector. + */ + + +/* Allow peeking at the hdr entry at the beginning of the current + * read index, without advancing the read index */ +static int vhdx_log_peek_hdr(BlockDriverState *bs, VHDXLogEntries *log, + VHDXLogEntryHeader *hdr) +{ + int ret = 0; + uint64_t offset; + uint32_t read; + + assert(hdr != NULL); + + /* peek is only supported on sector boundaries */ + if (log->read % VHDX_LOG_SECTOR_SIZE) { + ret = -EFAULT; + goto exit; + } + + read = log->read; + /* we are guaranteed that a) log sectors are 4096 bytes, + * and b) the log length is a multiple of 1MB. So, there + * is always a round number of sectors in the buffer */ + if ((read + sizeof(VHDXLogEntryHeader)) > log->length) { + read = 0; + } + + if (read == log->write) { + ret = -EINVAL; + goto exit; + } + + offset = log->offset + read; + + ret = bdrv_pread(bs->file, offset, hdr, sizeof(VHDXLogEntryHeader)); + if (ret < 0) { + goto exit; + } + +exit: + return ret; +} + +/* Index increment for log, based on sector boundaries */ +static int vhdx_log_inc_idx(uint32_t idx, uint64_t length) +{ + idx += VHDX_LOG_SECTOR_SIZE; + /* we are guaranteed that a) log sectors are 4096 bytes, + * and b) the log length is a multiple of 1MB. So, there + * is always a round number of sectors in the buffer */ + return idx >= length ? 0 : idx; +} + + +/* Reset the log to empty */ +static void vhdx_log_reset(BlockDriverState *bs, BDRVVHDXState *s) +{ + MSGUID guid = { 0 }; + s->log.read = s->log.write = 0; + /* a log guid of 0 indicates an empty log to any parser of v0 + * VHDX logs */ + vhdx_update_headers(bs, s, false, &guid); +} + +/* Reads num_sectors from the log (all log sectors are 4096 bytes), + * into buffer 'buffer'. Upon return, *sectors_read will contain + * the number of sectors successfully read. + * + * It is assumed that 'buffer' is already allocated, and of sufficient + * size (i.e. >= 4096*num_sectors). + * + * If 'peek' is true, then the tail (read) pointer for the circular buffer is + * not modified. + * + * 0 is returned on success, -errno otherwise. */ +static int vhdx_log_read_sectors(BlockDriverState *bs, VHDXLogEntries *log, + uint32_t *sectors_read, void *buffer, + uint32_t num_sectors, bool peek) +{ + int ret = 0; + uint64_t offset; + uint32_t read; + + read = log->read; + + *sectors_read = 0; + while (num_sectors) { + if (read == log->write) { + /* empty */ + break; + } + offset = log->offset + read; + + ret = bdrv_pread(bs->file, offset, buffer, VHDX_LOG_SECTOR_SIZE); + if (ret < 0) { + goto exit; + } + read = vhdx_log_inc_idx(read, log->length); + + *sectors_read = *sectors_read + 1; + num_sectors--; + } + +exit: + if (!peek) { + log->read = read; + } + return ret; +} + +/* Writes num_sectors to the log (all log sectors are 4096 bytes), + * from buffer 'buffer'. Upon return, *sectors_written will contain + * the number of sectors successfully written. + * + * It is assumed that 'buffer' is at least 4096*num_sectors large. + * + * 0 is returned on success, -errno otherwise */ +static int vhdx_log_write_sectors(BlockDriverState *bs, VHDXLogEntries *log, + uint32_t *sectors_written, void *buffer, + uint32_t num_sectors) +{ + int ret = 0; + uint64_t offset; + uint32_t write; + void *buffer_tmp; + BDRVVHDXState *s = bs->opaque; + + ret = vhdx_user_visible_write(bs, s); + if (ret < 0) { + goto exit; + } + + write = log->write; + + buffer_tmp = buffer; + while (num_sectors) { + + offset = log->offset + write; + write = vhdx_log_inc_idx(write, log->length); + if (write == log->read) { + /* full */ + break; + } + ret = bdrv_pwrite(bs->file, offset, buffer_tmp, VHDX_LOG_SECTOR_SIZE); + if (ret < 0) { + goto exit; + } + buffer_tmp += VHDX_LOG_SECTOR_SIZE; + + log->write = write; + *sectors_written = *sectors_written + 1; + num_sectors--; + } + +exit: + return ret; +} + + +/* Validates a log entry header */ +static bool vhdx_log_hdr_is_valid(VHDXLogEntries *log, VHDXLogEntryHeader *hdr, + BDRVVHDXState *s) +{ + int valid = false; + + if (memcmp(&hdr->signature, "loge", 4)) { + goto exit; + } + + /* if the individual entry length is larger than the whole log + * buffer, that is obviously invalid */ + if (log->length < hdr->entry_length) { + goto exit; + } + + /* length of entire entry must be in units of 4KB (log sector size) */ + if (hdr->entry_length % (VHDX_LOG_SECTOR_SIZE)) { + goto exit; + } + + /* per spec, sequence # must be > 0 */ + if (hdr->sequence_number == 0) { + goto exit; + } + + /* log entries are only valid if they match the file-wide log guid + * found in the active header */ + if (!guid_eq(hdr->log_guid, s->headers[s->curr_header]->log_guid)) { + goto exit; + } + + if (hdr->descriptor_count * sizeof(VHDXLogDescriptor) > hdr->entry_length) { + goto exit; + } + + valid = true; + +exit: + return valid; +} + +/* + * Given a log header, this will validate that the descriptors and the + * corresponding data sectors (if applicable) + * + * Validation consists of: + * 1. Making sure the sequence numbers matches the entry header + * 2. Verifying a valid signature ('zero' or 'desc' for descriptors) + * 3. File offset field is a multiple of 4KB + * 4. If a data descriptor, the corresponding data sector + * has its signature ('data') and matching sequence number + * + * @desc: the data buffer containing the descriptor + * @hdr: the log entry header + * + * Returns true if valid + */ +static bool vhdx_log_desc_is_valid(VHDXLogDescriptor *desc, + VHDXLogEntryHeader *hdr) +{ + bool ret = false; + + if (desc->sequence_number != hdr->sequence_number) { + goto exit; + } + if (desc->file_offset % VHDX_LOG_SECTOR_SIZE) { + goto exit; + } + + if (!memcmp(&desc->signature, "zero", 4)) { + if (desc->zero_length % VHDX_LOG_SECTOR_SIZE == 0) { + /* valid */ + ret = true; + } + } else if (!memcmp(&desc->signature, "desc", 4)) { + /* valid */ + ret = true; + } + +exit: + return ret; +} + + +/* Prior to sector data for a log entry, there is the header + * and the descriptors referenced in the header: + * + * [] = 4KB sector + * + * [ hdr, desc ][ desc ][ ... ][ data ][ ... ] + * + * The first sector in a log entry has a 64 byte header, and + * up to 126 32-byte descriptors. If more descriptors than + * 126 are required, then subsequent sectors can have up to 128 + * descriptors. Each sector is 4KB. Data follows the descriptor + * sectors. + * + * This will return the number of sectors needed to encompass + * the passed number of descriptors in desc_cnt. + * + * This will never return 0, even if desc_cnt is 0. + */ +static int vhdx_compute_desc_sectors(uint32_t desc_cnt) +{ + uint32_t desc_sectors; + + desc_cnt += 2; /* account for header in first sector */ + desc_sectors = desc_cnt / 128; + if (desc_cnt % 128) { + desc_sectors++; + } + + return desc_sectors; +} + + +/* Reads the log header, and subsequent descriptors (if any). This + * will allocate all the space for buffer, which must be NULL when + * passed into this function. Each descriptor will also be validated, + * and error returned if any are invalid. */ +static int vhdx_log_read_desc(BlockDriverState *bs, BDRVVHDXState *s, + VHDXLogEntries *log, VHDXLogDescEntries **buffer) +{ + int ret = 0; + uint32_t desc_sectors; + uint32_t sectors_read; + VHDXLogEntryHeader hdr; + VHDXLogDescEntries *desc_entries = NULL; + int i; + + assert(*buffer == NULL); + + ret = vhdx_log_peek_hdr(bs, log, &hdr); + if (ret < 0) { + goto exit; + } + vhdx_log_entry_hdr_le_import(&hdr); + if (vhdx_log_hdr_is_valid(log, &hdr, s) == false) { + ret = -EINVAL; + goto exit; + } + + desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count); + desc_entries = qemu_blockalign(bs, desc_sectors * VHDX_LOG_SECTOR_SIZE); + + ret = vhdx_log_read_sectors(bs, log, §ors_read, desc_entries, + desc_sectors, false); + if (ret < 0) { + goto free_and_exit; + } + if (sectors_read != desc_sectors) { + ret = -EINVAL; + goto free_and_exit; + } + + /* put in proper endianness, and validate each desc */ + for (i = 0; i < hdr.descriptor_count; i++) { + vhdx_log_desc_le_import(&desc_entries->desc[i]); + if (vhdx_log_desc_is_valid(&desc_entries->desc[i], &hdr) == false) { + ret = -EINVAL; + goto free_and_exit; + } + } + + *buffer = desc_entries; + goto exit; + +free_and_exit: + qemu_vfree(desc_entries); +exit: + return ret; +} + + +/* Flushes the descriptor described by desc to the VHDX image file. + * If the descriptor is a data descriptor, than 'data' must be non-NULL, + * and >= 4096 bytes (VHDX_LOG_SECTOR_SIZE), containing the data to be + * written. + * + * Verification is performed to make sure the sequence numbers of a data + * descriptor match the sequence number in the desc. + * + * For a zero descriptor, it may describe multiple sectors to fill with zeroes. + * In this case, it should be noted that zeroes are written to disk, and the + * image file is not extended as a sparse file. */ +static int vhdx_log_flush_desc(BlockDriverState *bs, VHDXLogDescriptor *desc, + VHDXLogDataSector *data) +{ + int ret = 0; + uint64_t seq, file_offset; + uint32_t offset = 0; + void *buffer = NULL; + uint64_t count = 1; + int i; + + buffer = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE); + + if (!memcmp(&desc->signature, "desc", 4)) { + /* data sector */ + if (data == NULL) { + ret = -EFAULT; + goto exit; + } + + /* The sequence number of the data sector must match that + * in the descriptor */ + seq = data->sequence_high; + seq <<= 32; + seq |= data->sequence_low & 0xffffffff; + + if (seq != desc->sequence_number) { + ret = -EINVAL; + goto exit; + } + + /* Each data sector is in total 4096 bytes, however the first + * 8 bytes, and last 4 bytes, are located in the descriptor */ + memcpy(buffer, &desc->leading_bytes, 8); + offset += 8; + + memcpy(buffer+offset, data->data, 4084); + offset += 4084; + + memcpy(buffer+offset, &desc->trailing_bytes, 4); + + } else if (!memcmp(&desc->signature, "zero", 4)) { + /* write 'count' sectors of sector */ + memset(buffer, 0, VHDX_LOG_SECTOR_SIZE); + count = desc->zero_length / VHDX_LOG_SECTOR_SIZE; + } + + file_offset = desc->file_offset; + + /* count is only > 1 if we are writing zeroes */ + for (i = 0; i < count; i++) { + ret = bdrv_pwrite_sync(bs->file, file_offset, buffer, + VHDX_LOG_SECTOR_SIZE); + if (ret < 0) { + goto exit; + } + file_offset += VHDX_LOG_SECTOR_SIZE; + } + +exit: + qemu_vfree(buffer); + return ret; +} + +/* Flush the entire log (as described by 'logs') to the VHDX image + * file, and then set the log to 'empty' status once complete. + * + * The log entries should be validate prior to flushing */ +static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s, + VHDXLogSequence *logs) +{ + int ret = 0; + int i; + uint32_t cnt, sectors_read; + uint64_t new_file_size; + void *data = NULL; + VHDXLogDescEntries *desc_entries = NULL; + VHDXLogEntryHeader hdr_tmp = { 0 }; + + cnt = logs->count; + + data = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE); + + ret = vhdx_user_visible_write(bs, s); + if (ret < 0) { + goto exit; + } + + /* each iteration represents one log sequence, which may span multiple + * sectors */ + while (cnt--) { + ret = vhdx_log_peek_hdr(bs, &logs->log, &hdr_tmp); + if (ret < 0) { + goto exit; + } + /* if the log shows a FlushedFileOffset larger than our current file + * size, then that means the file has been truncated / corrupted, and + * we must refused to open it / use it */ + if (hdr_tmp.flushed_file_offset > bdrv_getlength(bs->file)) { + ret = -EINVAL; + goto exit; + } + + ret = vhdx_log_read_desc(bs, s, &logs->log, &desc_entries); + if (ret < 0) { + goto exit; + } + + for (i = 0; i < desc_entries->hdr.descriptor_count; i++) { + if (!memcmp(&desc_entries->desc[i].signature, "desc", 4)) { + /* data sector, so read a sector to flush */ + ret = vhdx_log_read_sectors(bs, &logs->log, §ors_read, + data, 1, false); + if (ret < 0) { + goto exit; + } + if (sectors_read != 1) { + ret = -EINVAL; + goto exit; + } + } + + ret = vhdx_log_flush_desc(bs, &desc_entries->desc[i], data); + if (ret < 0) { + goto exit; + } + } + if (bdrv_getlength(bs->file) < desc_entries->hdr.last_file_offset) { + new_file_size = desc_entries->hdr.last_file_offset; + if (new_file_size % (1024*1024)) { + /* round up to nearest 1MB boundary */ + new_file_size = ((new_file_size >> 20) + 1) << 20; + bdrv_truncate(bs->file, new_file_size); + } + } + qemu_vfree(desc_entries); + desc_entries = NULL; + } + + bdrv_flush(bs); + /* once the log is fully flushed, indicate that we have an empty log + * now. This also sets the log guid to 0, to indicate an empty log */ + vhdx_log_reset(bs, s); + +exit: + qemu_vfree(data); + qemu_vfree(desc_entries); + return ret; +} + +static int vhdx_validate_log_entry(BlockDriverState *bs, BDRVVHDXState *s, + VHDXLogEntries *log, uint64_t seq, + bool *valid, VHDXLogEntryHeader *entry) +{ + int ret = 0; + VHDXLogEntryHeader hdr; + void *buffer = NULL; + uint32_t i, desc_sectors, total_sectors, crc; + uint32_t sectors_read = 0; + VHDXLogDescEntries *desc_buffer = NULL; + + *valid = false; + + ret = vhdx_log_peek_hdr(bs, log, &hdr); + if (ret < 0) { + goto inc_and_exit; + } + + vhdx_log_entry_hdr_le_import(&hdr); + + + if (vhdx_log_hdr_is_valid(log, &hdr, s) == false) { + goto inc_and_exit; + } + + if (seq > 0) { + if (hdr.sequence_number != seq + 1) { + goto inc_and_exit; + } + } + + desc_sectors = vhdx_compute_desc_sectors(hdr.descriptor_count); + + /* Read desc sectors, and calculate log checksum */ + + total_sectors = hdr.entry_length / VHDX_LOG_SECTOR_SIZE; + + + /* read_desc() will incrememnt the read idx */ + ret = vhdx_log_read_desc(bs, s, log, &desc_buffer); + if (ret < 0) { + goto free_and_exit; + } + + crc = vhdx_checksum_calc(0xffffffff, (void *)desc_buffer, + desc_sectors * VHDX_LOG_SECTOR_SIZE, 4); + crc ^= 0xffffffff; + + buffer = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE); + if (total_sectors > desc_sectors) { + for (i = 0; i < total_sectors - desc_sectors; i++) { + sectors_read = 0; + ret = vhdx_log_read_sectors(bs, log, §ors_read, buffer, + 1, false); + if (ret < 0 || sectors_read != 1) { + goto free_and_exit; + } + crc = vhdx_checksum_calc(crc, buffer, VHDX_LOG_SECTOR_SIZE, -1); + crc ^= 0xffffffff; + } + } + crc ^= 0xffffffff; + if (crc != desc_buffer->hdr.checksum) { + goto free_and_exit; + } + + *valid = true; + *entry = hdr; + goto free_and_exit; + +inc_and_exit: + log->read = vhdx_log_inc_idx(log->read, log->length); + +free_and_exit: + qemu_vfree(buffer); + qemu_vfree(desc_buffer); + return ret; +} + +/* Search through the log circular buffer, and find the valid, active + * log sequence, if any exists + * */ +static int vhdx_log_search(BlockDriverState *bs, BDRVVHDXState *s, + VHDXLogSequence *logs) +{ + int ret = 0; + uint32_t tail; + bool seq_valid = false; + VHDXLogSequence candidate = { 0 }; + VHDXLogEntryHeader hdr = { 0 }; + VHDXLogEntries curr_log; + + memcpy(&curr_log, &s->log, sizeof(VHDXLogEntries)); + curr_log.write = curr_log.length; /* assume log is full */ + curr_log.read = 0; + + + /* now we will go through the whole log sector by sector, until + * we find a valid, active log sequence, or reach the end of the + * log buffer */ + for (;;) { + uint64_t curr_seq = 0; + VHDXLogSequence current = { 0 }; + + tail = curr_log.read; + + ret = vhdx_validate_log_entry(bs, s, &curr_log, curr_seq, + &seq_valid, &hdr); + if (ret < 0) { + goto exit; + } + + if (seq_valid) { + current.valid = true; + current.log = curr_log; + current.log.read = tail; + current.log.write = curr_log.read; + current.count = 1; + current.hdr = hdr; + + + for (;;) { + ret = vhdx_validate_log_entry(bs, s, &curr_log, curr_seq, + &seq_valid, &hdr); + if (ret < 0) { + goto exit; + } + if (seq_valid == false) { + break; + } + current.log.write = curr_log.read; + current.count++; + + curr_seq = hdr.sequence_number; + } + } + + if (current.valid) { + if (candidate.valid == false || + current.hdr.sequence_number > candidate.hdr.sequence_number) { + candidate = current; + } + } + + if (curr_log.read < tail) { + break; + } + } + + *logs = candidate; + + if (candidate.valid) { + /* this is the next sequence number, for writes */ + s->log.sequence = candidate.hdr.sequence_number + 1; + } + + +exit: + return ret; +} + +/* Parse the replay log. Per the VHDX spec, if the log is present + * it must be replayed prior to opening the file, even read-only. + * + * If read-only, we must replay the log in RAM (or refuse to open + * a dirty VHDX file read-only) */ +int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed) +{ + int ret = 0; + VHDXHeader *hdr; + VHDXLogSequence logs = { 0 }; + + hdr = s->headers[s->curr_header]; + + *flushed = false; + + /* s->log.hdr is freed in vhdx_close() */ + if (s->log.hdr == NULL) { + s->log.hdr = qemu_blockalign(bs, sizeof(VHDXLogEntryHeader)); + } + + s->log.offset = hdr->log_offset; + s->log.length = hdr->log_length; + + if (s->log.offset < VHDX_LOG_MIN_SIZE || + s->log.offset % VHDX_LOG_MIN_SIZE) { + ret = -EINVAL; + goto exit; + } + + /* per spec, only log version of 0 is supported */ + if (hdr->log_version != 0) { + ret = -EINVAL; + goto exit; + } + + /* If either the log guid, or log length is zero, + * then a replay log is not present */ + if (guid_eq(hdr->log_guid, zero_guid)) { + goto exit; + } + + if (hdr->log_length == 0) { + goto exit; + } + + if (hdr->log_length % VHDX_LOG_MIN_SIZE) { + ret = -EINVAL; + goto exit; + } + + + /* The log is present, we need to find if and where there is an active + * sequence of valid entries present in the log. */ + + ret = vhdx_log_search(bs, s, &logs); + if (ret < 0) { + goto exit; + } + + if (logs.valid) { + /* now flush the log */ + ret = vhdx_log_flush(bs, s, &logs); + if (ret < 0) { + goto exit; + } + *flushed = true; + } + + +exit: + return ret; +} + + + +static void vhdx_log_raw_to_le_sector(VHDXLogDescriptor *desc, + VHDXLogDataSector *sector, void *data, + uint64_t seq) +{ + /* 8 + 4084 + 4 = 4096, 1 log sector */ + memcpy(&desc->leading_bytes, data, 8); + data += 8; + cpu_to_le64s(&desc->leading_bytes); + memcpy(sector->data, data, 4084); + data += 4084; + memcpy(&desc->trailing_bytes, data, 4); + cpu_to_le32s(&desc->trailing_bytes); + data += 4; + + sector->sequence_high = (uint32_t) (seq >> 32); + sector->sequence_low = (uint32_t) (seq & 0xffffffff); + sector->data_signature = VHDX_LOG_DATA_SIGNATURE; + + vhdx_log_desc_le_export(desc); + vhdx_log_data_le_export(sector); +} + + +static int vhdx_log_write(BlockDriverState *bs, BDRVVHDXState *s, + void *data, uint32_t length, uint64_t offset) +{ + int ret = 0; + void *buffer = NULL; + void *merged_sector = NULL; + void *data_tmp, *sector_write; + unsigned int i; + int sector_offset; + uint32_t desc_sectors, sectors, total_length; + uint32_t sectors_written = 0; + uint32_t aligned_length; + uint32_t leading_length = 0; + uint32_t trailing_length = 0; + uint32_t partial_sectors = 0; + uint32_t bytes_written = 0; + uint64_t file_offset; + VHDXHeader *header; + VHDXLogEntryHeader new_hdr; + VHDXLogDescriptor *new_desc = NULL; + VHDXLogDataSector *data_sector = NULL; + MSGUID new_guid = { 0 }; + + header = s->headers[s->curr_header]; + + /* need to have offset read data, and be on 4096 byte boundary */ + + if (length > header->log_length) { + /* no log present. we could create a log here instead of failing */ + ret = -EINVAL; + goto exit; + } + + if (guid_eq(header->log_guid, zero_guid)) { + vhdx_guid_generate(&new_guid); + vhdx_update_headers(bs, s, false, &new_guid); + } else { + /* currently, we require that the log be flushed after + * every write. */ + ret = -ENOTSUP; + goto exit; + } + + /* 0 is an invalid sequence number, but may also represent the first + * log write (or a wrapped seq) */ + if (s->log.sequence == 0) { + s->log.sequence = 1; + } + + sector_offset = offset % VHDX_LOG_SECTOR_SIZE; + file_offset = (offset / VHDX_LOG_SECTOR_SIZE) * VHDX_LOG_SECTOR_SIZE; + + aligned_length = length; + + /* add in the unaligned head and tail bytes */ + if (sector_offset) { + leading_length = (VHDX_LOG_SECTOR_SIZE - sector_offset); + leading_length = leading_length > length ? length : leading_length; + aligned_length -= leading_length; + partial_sectors++; + } + + sectors = aligned_length / VHDX_LOG_SECTOR_SIZE; + trailing_length = aligned_length - (sectors * VHDX_LOG_SECTOR_SIZE); + if (trailing_length) { + partial_sectors++; + } + + sectors += partial_sectors; + + /* sectors is now how many sectors the data itself takes, not + * including the header and descriptor metadata */ + + new_hdr = (VHDXLogEntryHeader) { + .signature = VHDX_LOG_SIGNATURE, + .tail = s->log.tail, + .sequence_number = s->log.sequence, + .descriptor_count = sectors, + .reserved = 0, + .flushed_file_offset = bdrv_getlength(bs->file), + .last_file_offset = bdrv_getlength(bs->file), + }; + + new_hdr.log_guid = header->log_guid; + + desc_sectors = vhdx_compute_desc_sectors(new_hdr.descriptor_count); + + total_length = (desc_sectors + sectors) * VHDX_LOG_SECTOR_SIZE; + new_hdr.entry_length = total_length; + + vhdx_log_entry_hdr_le_export(&new_hdr); + + buffer = qemu_blockalign(bs, total_length); + memcpy(buffer, &new_hdr, sizeof(new_hdr)); + + new_desc = (VHDXLogDescriptor *) (buffer + sizeof(new_hdr)); + data_sector = buffer + (desc_sectors * VHDX_LOG_SECTOR_SIZE); + data_tmp = data; + + /* All log sectors are 4KB, so for any partial sectors we must + * merge the data with preexisting data from the final file + * destination */ + merged_sector = qemu_blockalign(bs, VHDX_LOG_SECTOR_SIZE); + + for (i = 0; i < sectors; i++) { + new_desc->signature = VHDX_LOG_DESC_SIGNATURE; + new_desc->sequence_number = s->log.sequence; + new_desc->file_offset = file_offset; + + if (i == 0 && leading_length) { + /* partial sector at the front of the buffer */ + ret = bdrv_pread(bs->file, file_offset, merged_sector, + VHDX_LOG_SECTOR_SIZE); + if (ret < 0) { + goto exit; + } + memcpy(merged_sector + sector_offset, data_tmp, leading_length); + bytes_written = leading_length; + sector_write = merged_sector; + } else if (i == sectors - 1 && trailing_length) { + /* partial sector at the end of the buffer */ + ret = bdrv_pread(bs->file, + file_offset, + merged_sector + trailing_length, + VHDX_LOG_SECTOR_SIZE - trailing_length); + if (ret < 0) { + goto exit; + } + memcpy(merged_sector, data_tmp, trailing_length); + bytes_written = trailing_length; + sector_write = merged_sector; + } else { + bytes_written = VHDX_LOG_SECTOR_SIZE; + sector_write = data_tmp; + } + + /* populate the raw sector data into the proper structures, + * as well as update the descriptor, and convert to proper + * endianness */ + vhdx_log_raw_to_le_sector(new_desc, data_sector, sector_write, + s->log.sequence); + + data_tmp += bytes_written; + data_sector++; + new_desc++; + file_offset += VHDX_LOG_SECTOR_SIZE; + } + + /* checksum covers entire entry, from the log header through the + * last data sector */ + vhdx_update_checksum(buffer, total_length, + offsetof(VHDXLogEntryHeader, checksum)); + cpu_to_le32s((uint32_t *)(buffer + 4)); + + /* now write to the log */ + vhdx_log_write_sectors(bs, &s->log, §ors_written, buffer, + desc_sectors + sectors); + if (ret < 0) { + goto exit; + } + + if (sectors_written != desc_sectors + sectors) { + /* instead of failing, we could flush the log here */ + ret = -EINVAL; + goto exit; + } + + s->log.sequence++; + /* write new tail */ + s->log.tail = s->log.write; + +exit: + qemu_vfree(buffer); + qemu_vfree(merged_sector); + return ret; +} + +/* Perform a log write, and then immediately flush the entire log */ +int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, + void *data, uint32_t length, uint64_t offset) +{ + int ret = 0; + VHDXLogSequence logs = { .valid = true, + .count = 1, + .hdr = { 0 } }; + + + /* Make sure data written (new and/or changed blocks) is stable + * on disk, before creating log entry */ + bdrv_flush(bs); + ret = vhdx_log_write(bs, s, data, length, offset); + if (ret < 0) { + goto exit; + } + logs.log = s->log; + + /* Make sure log is stable on disk */ + bdrv_flush(bs); + ret = vhdx_log_flush(bs, s, &logs); + if (ret < 0) { + goto exit; + } + + s->log = logs.log; + +exit: + return ret; +} + diff --git a/block/vhdx.c b/block/vhdx.c index 6cb04122bb..7d1af9663b 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -6,9 +6,9 @@ * Authors: * Jeff Cody <jcody@redhat.com> * - * This is based on the "VHDX Format Specification v0.95", published 4/12/2012 + * This is based on the "VHDX Format Specification v1.00", published 8/25/2012 * by Microsoft: - * https://www.microsoft.com/en-us/download/details.aspx?id=29681 + * https://www.microsoft.com/en-us/download/details.aspx?id=34750 * * 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. @@ -22,6 +22,20 @@ #include "block/vhdx.h" #include "migration/migration.h" +#include <uuid/uuid.h> +#include <glib.h> + +/* Options for VHDX creation */ + +#define VHDX_BLOCK_OPT_LOG_SIZE "log_size" +#define VHDX_BLOCK_OPT_BLOCK_SIZE "block_size" +#define VHDX_BLOCK_OPT_ZERO "block_state_zero" + +typedef enum VHDXImageType { + VHDX_TYPE_DYNAMIC = 0, + VHDX_TYPE_FIXED, + VHDX_TYPE_DIFFERENCING, /* Currently unsupported */ +} VHDXImageType; /* Several metadata and region table data entries are identified by * guids in a MS-specific GUID format. */ @@ -104,16 +118,6 @@ static const MSGUID parent_vhdx_guid = { .data1 = 0xb04aefb7, META_PAGE_83_PRESENT | META_LOGICAL_SECTOR_SIZE_PRESENT | \ META_PHYS_SECTOR_SIZE_PRESENT) -typedef struct VHDXMetadataEntries { - VHDXMetadataTableEntry file_parameters_entry; - VHDXMetadataTableEntry virtual_disk_size_entry; - VHDXMetadataTableEntry page83_data_entry; - VHDXMetadataTableEntry logical_sector_size_entry; - VHDXMetadataTableEntry phys_sector_size_entry; - VHDXMetadataTableEntry parent_locator_entry; - uint16_t present; -} VHDXMetadataEntries; - typedef struct VHDXSectorInfo { uint32_t bat_idx; /* BAT entry index */ @@ -124,44 +128,31 @@ typedef struct VHDXSectorInfo { uint64_t block_offset; /* block offset, in bytes */ } VHDXSectorInfo; +/* Calculates new checksum. + * + * Zero is substituted during crc calculation for the original crc field + * crc_offset: byte offset in buf of the buffer crc + * buf: buffer pointer + * size: size of buffer (must be > crc_offset+4) + * + * Note: The resulting checksum is in the CPU endianness, not necessarily + * in the file format endianness (LE). Any header export to disk should + * make sure that vhdx_header_le_export() is used to convert to the + * correct endianness + */ +uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset) +{ + uint32_t crc; + assert(buf != NULL); + assert(size > (crc_offset + sizeof(crc))); -typedef struct BDRVVHDXState { - CoMutex lock; - - int curr_header; - VHDXHeader *headers[2]; - - VHDXRegionTableHeader rt; - VHDXRegionTableEntry bat_rt; /* region table for the BAT */ - VHDXRegionTableEntry metadata_rt; /* region table for the metadata */ - - VHDXMetadataTableHeader metadata_hdr; - VHDXMetadataEntries metadata_entries; - - VHDXFileParameters params; - uint32_t block_size; - uint32_t block_size_bits; - uint32_t sectors_per_block; - uint32_t sectors_per_block_bits; - - uint64_t virtual_disk_size; - uint32_t logical_sector_size; - uint32_t physical_sector_size; - - uint64_t chunk_ratio; - uint32_t chunk_ratio_bits; - uint32_t logical_sector_size_bits; - - uint32_t bat_entries; - VHDXBatEntry *bat; - uint64_t bat_offset; - - VHDXParentLocatorHeader parent_header; - VHDXParentLocatorEntry *parent_entries; + memset(buf + crc_offset, 0, sizeof(crc)); + crc = crc32c(0xffffffff, buf, size); + memcpy(buf + crc_offset, &crc, sizeof(crc)); - Error *migration_blocker; -} BDRVVHDXState; + return crc; +} uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, int crc_offset) @@ -214,6 +205,71 @@ bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset) /* + * This generates a UUID that is compliant with the MS GUIDs used + * in the VHDX spec (and elsewhere). + */ +void vhdx_guid_generate(MSGUID *guid) +{ + uuid_t uuid; + assert(guid != NULL); + + uuid_generate(uuid); + memcpy(guid, uuid, sizeof(MSGUID)); +} + +/* Check for region overlaps inside the VHDX image */ +static int vhdx_region_check(BDRVVHDXState *s, uint64_t start, uint64_t length) +{ + int ret = 0; + uint64_t end; + VHDXRegionEntry *r; + + end = start + length; + QLIST_FOREACH(r, &s->regions, entries) { + if (!((start >= r->end) || (end <= r->start))) { + ret = -EINVAL; + goto exit; + } + } + +exit: + return ret; +} + +/* Register a region for future checks */ +static void vhdx_region_register(BDRVVHDXState *s, + uint64_t start, uint64_t length) +{ + VHDXRegionEntry *r; + + r = g_malloc0(sizeof(*r)); + + r->start = start; + r->end = start + length; + + QLIST_INSERT_HEAD(&s->regions, r, entries); +} + +/* Free all registered regions */ +static void vhdx_region_unregister_all(BDRVVHDXState *s) +{ + VHDXRegionEntry *r, *r_next; + + QLIST_FOREACH_SAFE(r, &s->regions, entries, r_next) { + QLIST_REMOVE(r, entries); + g_free(r); + } +} + +static void vhdx_set_shift_bits(BDRVVHDXState *s) +{ + s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size); + s->sectors_per_block_bits = 31 - clz32(s->sectors_per_block); + s->chunk_ratio_bits = 63 - clz64(s->chunk_ratio); + s->block_size_bits = 31 - clz32(s->block_size); +} + +/* * Per the MS VHDX Specification, for every VHDX file: * - The header section is fixed size - 1 MB * - The header section is always the first "object" @@ -232,25 +288,118 @@ static int vhdx_probe(const uint8_t *buf, int buf_size, const char *filename) return 0; } -/* All VHDX structures on disk are little endian */ -static void vhdx_header_le_import(VHDXHeader *h) +/* + * Writes the header to the specified offset. + * + * This will optionally read in buffer data from disk (otherwise zero-fill), + * and then update the header checksum. Header is converted to proper + * endianness before being written to the specified file offset + */ +static int vhdx_write_header(BlockDriverState *bs_file, VHDXHeader *hdr, + uint64_t offset, bool read) +{ + uint8_t *buffer = NULL; + int ret; + VHDXHeader header_le; + + assert(bs_file != NULL); + assert(hdr != NULL); + + /* the header checksum is not over just the packed size of VHDXHeader, + * but rather over the entire 'reserved' range for the header, which is + * 4KB (VHDX_HEADER_SIZE). */ + + buffer = qemu_blockalign(bs_file, VHDX_HEADER_SIZE); + if (read) { + /* if true, we can't assume the extra reserved bytes are 0 */ + ret = bdrv_pread(bs_file, offset, buffer, VHDX_HEADER_SIZE); + if (ret < 0) { + goto exit; + } + } else { + memset(buffer, 0, VHDX_HEADER_SIZE); + } + + /* overwrite the actual VHDXHeader portion */ + memcpy(buffer, hdr, sizeof(VHDXHeader)); + hdr->checksum = vhdx_update_checksum(buffer, VHDX_HEADER_SIZE, + offsetof(VHDXHeader, checksum)); + vhdx_header_le_export(hdr, &header_le); + ret = bdrv_pwrite_sync(bs_file, offset, &header_le, sizeof(VHDXHeader)); + +exit: + qemu_vfree(buffer); + return ret; +} + +/* Update the VHDX headers + * + * This follows the VHDX spec procedures for header updates. + * + * - non-current header is updated with largest sequence number + */ +static int vhdx_update_header(BlockDriverState *bs, BDRVVHDXState *s, + bool generate_data_write_guid, MSGUID *log_guid) { - assert(h != NULL); + int ret = 0; + int hdr_idx = 0; + uint64_t header_offset = VHDX_HEADER1_OFFSET; + + VHDXHeader *active_header; + VHDXHeader *inactive_header; + + /* operate on the non-current header */ + if (s->curr_header == 0) { + hdr_idx = 1; + header_offset = VHDX_HEADER2_OFFSET; + } + + active_header = s->headers[s->curr_header]; + inactive_header = s->headers[hdr_idx]; + + inactive_header->sequence_number = active_header->sequence_number + 1; + + /* a new file guid must be generated before any file write, including + * headers */ + inactive_header->file_write_guid = s->session_guid; + + /* a new data guid only needs to be generated before any guest-visible + * writes (i.e. something observable via virtual disk read) */ + if (generate_data_write_guid) { + vhdx_guid_generate(&inactive_header->data_write_guid); + } - le32_to_cpus(&h->signature); - le32_to_cpus(&h->checksum); - le64_to_cpus(&h->sequence_number); + /* update the log guid if present */ + if (log_guid) { + inactive_header->log_guid = *log_guid; + } - leguid_to_cpus(&h->file_write_guid); - leguid_to_cpus(&h->data_write_guid); - leguid_to_cpus(&h->log_guid); + vhdx_write_header(bs->file, inactive_header, header_offset, true); + if (ret < 0) { + goto exit; + } + s->curr_header = hdr_idx; - le16_to_cpus(&h->log_version); - le16_to_cpus(&h->version); - le32_to_cpus(&h->log_length); - le64_to_cpus(&h->log_offset); +exit: + return ret; } +/* + * The VHDX spec calls for header updates to be performed twice, so that both + * the current and non-current header have valid info + */ +int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, + bool generate_data_write_guid, MSGUID *log_guid) +{ + int ret; + + ret = vhdx_update_header(bs, s, generate_data_write_guid, log_guid); + if (ret < 0) { + return ret; + } + ret = vhdx_update_header(bs, s, generate_data_write_guid, log_guid); + return ret; +} /* opens the specified header block from the VHDX file header section */ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s) @@ -264,6 +413,7 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s) uint64_t h2_seq = 0; uint8_t *buffer; + /* header1 & header2 are freed in vhdx_close() */ header1 = qemu_blockalign(bs, sizeof(VHDXHeader)); header2 = qemu_blockalign(bs, sizeof(VHDXHeader)); @@ -328,6 +478,9 @@ static int vhdx_parse_header(BlockDriverState *bs, BDRVVHDXState *s) } } + vhdx_region_register(s, s->headers[s->curr_header]->log_offset, + s->headers[s->curr_header]->log_length); + ret = 0; goto exit; @@ -364,10 +517,7 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) goto fail; } memcpy(&s->rt, buffer, sizeof(s->rt)); - le32_to_cpus(&s->rt.signature); - le32_to_cpus(&s->rt.checksum); - le32_to_cpus(&s->rt.entry_count); - le32_to_cpus(&s->rt.reserved); + vhdx_region_header_le_import(&s->rt); offset += sizeof(s->rt); if (!vhdx_checksum_is_valid(buffer, VHDX_HEADER_BLOCK_SIZE, 4) || @@ -386,10 +536,16 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) memcpy(&rt_entry, buffer + offset, sizeof(rt_entry)); offset += sizeof(rt_entry); - leguid_to_cpus(&rt_entry.guid); - le64_to_cpus(&rt_entry.file_offset); - le32_to_cpus(&rt_entry.length); - le32_to_cpus(&rt_entry.data_bits); + vhdx_region_entry_le_import(&rt_entry); + + /* check for region overlap between these entries, and any + * other memory regions in the file */ + ret = vhdx_region_check(s, rt_entry.file_offset, rt_entry.length); + if (ret < 0) { + goto fail; + } + + vhdx_region_register(s, rt_entry.file_offset, rt_entry.length); /* see if we recognize the entry */ if (guid_eq(rt_entry.guid, bat_guid)) { @@ -421,6 +577,12 @@ static int vhdx_open_region_tables(BlockDriverState *bs, BDRVVHDXState *s) goto fail; } } + + if (!bat_rt_found || !metadata_rt_found) { + ret = -EINVAL; + goto fail; + } + ret = 0; fail: @@ -464,9 +626,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) memcpy(&s->metadata_hdr, buffer, sizeof(s->metadata_hdr)); offset += sizeof(s->metadata_hdr); - le64_to_cpus(&s->metadata_hdr.signature); - le16_to_cpus(&s->metadata_hdr.reserved); - le16_to_cpus(&s->metadata_hdr.entry_count); + vhdx_metadata_header_le_import(&s->metadata_hdr); if (memcmp(&s->metadata_hdr.signature, "metadata", 8)) { ret = -EINVAL; @@ -485,11 +645,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) memcpy(&md_entry, buffer + offset, sizeof(md_entry)); offset += sizeof(md_entry); - leguid_to_cpus(&md_entry.item_id); - le32_to_cpus(&md_entry.offset); - le32_to_cpus(&md_entry.length); - le32_to_cpus(&md_entry.data_bits); - le32_to_cpus(&md_entry.reserved2); + vhdx_metadata_entry_le_import(&md_entry); if (guid_eq(md_entry.item_id, file_param_guid)) { if (s->metadata_entries.present & META_FILE_PARAMETER_PRESENT) { @@ -662,10 +818,7 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) goto exit; } - s->logical_sector_size_bits = 31 - clz32(s->logical_sector_size); - s->sectors_per_block_bits = 31 - clz32(s->sectors_per_block); - s->chunk_ratio_bits = 63 - clz64(s->chunk_ratio); - s->block_size_bits = 31 - clz32(s->block_size); + vhdx_set_shift_bits(s); ret = 0; @@ -674,48 +827,49 @@ exit: return ret; } -/* Parse the replay log. Per the VHDX spec, if the log is present - * it must be replayed prior to opening the file, even read-only. - * - * If read-only, we must replay the log in RAM (or refuse to open - * a dirty VHDX file read-only */ -static int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s) +/* + * Calculate the number of BAT entries, including sector + * bitmap entries. + */ +static void vhdx_calc_bat_entries(BDRVVHDXState *s) { - int ret = 0; - int i; - VHDXHeader *hdr; - - hdr = s->headers[s->curr_header]; + uint32_t data_blocks_cnt, bitmap_blocks_cnt; - /* either the log guid, or log length is zero, - * then a replay log is present */ - for (i = 0; i < sizeof(hdr->log_guid.data4); i++) { - ret |= hdr->log_guid.data4[i]; - } - if (hdr->log_guid.data1 == 0 && - hdr->log_guid.data2 == 0 && - hdr->log_guid.data3 == 0 && - ret == 0) { - goto exit; + data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits; + if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) { + data_blocks_cnt++; } - - /* per spec, only log version of 0 is supported */ - if (hdr->log_version != 0) { - ret = -EINVAL; - goto exit; + bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits; + if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) { + bitmap_blocks_cnt++; } - if (hdr->log_length == 0) { - goto exit; + if (s->parent_entries) { + s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1); + } else { + s->bat_entries = data_blocks_cnt + + ((data_blocks_cnt - 1) >> s->chunk_ratio_bits); } - /* We currently do not support images with logs to replay */ - ret = -ENOTSUP; - -exit: - return ret; } +static void vhdx_close(BlockDriverState *bs) +{ + BDRVVHDXState *s = bs->opaque; + qemu_vfree(s->headers[0]); + s->headers[0] = NULL; + qemu_vfree(s->headers[1]); + s->headers[1] = NULL; + qemu_vfree(s->bat); + s->bat = NULL; + qemu_vfree(s->parent_entries); + s->parent_entries = NULL; + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); + qemu_vfree(s->log.hdr); + s->log.hdr = NULL; + vhdx_region_unregister_all(s); +} static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) @@ -724,12 +878,14 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, int ret = 0; uint32_t i; uint64_t signature; - uint32_t data_blocks_cnt, bitmap_blocks_cnt; + bool log_flushed = false; s->bat = NULL; + s->first_visible_write = true; qemu_co_mutex_init(&s->lock); + QLIST_INIT(&s->regions); /* validate the file signature */ ret = bdrv_pread(bs->file, 0, &signature, sizeof(uint64_t)); @@ -741,46 +897,38 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + /* This is used for any header updates, for the file_write_guid. + * The spec dictates that a new value should be used for the first + * header update */ + vhdx_guid_generate(&s->session_guid); + ret = vhdx_parse_header(bs, s); - if (ret) { + if (ret < 0) { goto fail; } - ret = vhdx_parse_log(bs, s); - if (ret) { + ret = vhdx_parse_log(bs, s, &log_flushed); + if (ret < 0) { goto fail; } ret = vhdx_open_region_tables(bs, s); - if (ret) { + if (ret < 0) { goto fail; } ret = vhdx_parse_metadata(bs, s); - if (ret) { + if (ret < 0) { goto fail; } + s->block_size = s->params.block_size; /* the VHDX spec dictates that virtual_disk_size is always a multiple of * logical_sector_size */ bs->total_sectors = s->virtual_disk_size >> s->logical_sector_size_bits; - data_blocks_cnt = s->virtual_disk_size >> s->block_size_bits; - if (s->virtual_disk_size - (data_blocks_cnt << s->block_size_bits)) { - data_blocks_cnt++; - } - bitmap_blocks_cnt = data_blocks_cnt >> s->chunk_ratio_bits; - if (data_blocks_cnt - (bitmap_blocks_cnt << s->chunk_ratio_bits)) { - bitmap_blocks_cnt++; - } - - if (s->parent_entries) { - s->bat_entries = bitmap_blocks_cnt * (s->chunk_ratio + 1); - } else { - s->bat_entries = data_blocks_cnt + - ((data_blocks_cnt - 1) >> s->chunk_ratio_bits); - } + vhdx_calc_bat_entries(s); s->bat_offset = s->bat_rt.file_offset; @@ -790,6 +938,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + /* s->bat is freed in vhdx_close() */ s->bat = qemu_blockalign(bs, s->bat_rt.length); ret = bdrv_pread(bs->file, s->bat_offset, s->bat, s->bat_rt.length); @@ -797,16 +946,36 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + uint64_t payblocks = s->chunk_ratio; + /* endian convert, and verify populated BAT field file offsets against + * region table and log entries */ for (i = 0; i < s->bat_entries; i++) { le64_to_cpus(&s->bat[i]); + if (payblocks--) { + /* payload bat entries */ + if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) == + PAYLOAD_BLOCK_FULLY_PRESENT) { + ret = vhdx_region_check(s, s->bat[i] & VHDX_BAT_FILE_OFF_MASK, + s->block_size); + if (ret < 0) { + goto fail; + } + } + } else { + payblocks = s->chunk_ratio; + /* Once differencing files are supported, verify sector bitmap + * blocks here */ + } } if (flags & BDRV_O_RDWR) { - ret = -ENOTSUP; - goto fail; + ret = vhdx_update_headers(bs, s, false, NULL); + if (ret < 0) { + goto fail; + } } - /* TODO: differencing files, write */ + /* TODO: differencing files */ /* Disable migration when VHDX images are used */ error_set(&s->migration_blocker, @@ -816,10 +985,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, return 0; fail: - qemu_vfree(s->headers[0]); - qemu_vfree(s->headers[1]); - qemu_vfree(s->bat); - qemu_vfree(s->parent_entries); + vhdx_close(bs); return ret; } @@ -859,7 +1025,7 @@ static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num, sinfo->bytes_avail = sinfo->sectors_avail << s->logical_sector_size_bits; - sinfo->file_offset = s->bat[sinfo->bat_idx] >> VHDX_BAT_FILE_OFF_BITS; + sinfo->file_offset = s->bat[sinfo->bat_idx] & VHDX_BAT_FILE_OFF_MASK; sinfo->block_offset = block_offset << s->logical_sector_size_bits; @@ -873,7 +1039,6 @@ static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num, * in the block, and add in the payload data block offset * in the file, in bytes, to get the final read address */ - sinfo->file_offset <<= 20; /* now in bytes, rather than 1MB units */ sinfo->file_offset += sinfo->block_offset; } @@ -914,7 +1079,7 @@ static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num, /* return zero */ qemu_iovec_memset(&hd_qiov, 0, 0, sinfo.bytes_avail); break; - case PAYLOAD_BLOCK_FULL_PRESENT: + case PAYLOAD_BLOCK_FULLY_PRESENT: qemu_co_mutex_unlock(&s->lock); ret = bdrv_co_readv(bs->file, sinfo.file_offset >> BDRV_SECTOR_BITS, @@ -944,26 +1109,772 @@ exit: return ret; } +/* + * Allocate a new payload block at the end of the file. + * + * Allocation will happen at 1MB alignment inside the file + * + * Returns the file offset start of the new payload block + */ +static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s, + uint64_t *new_offset) +{ + *new_offset = bdrv_getlength(bs->file); + + /* per the spec, the address for a block is in units of 1MB */ + *new_offset = ROUND_UP(*new_offset, 1024 * 1024); + + return bdrv_truncate(bs->file, *new_offset + s->block_size); +} + +/* + * Update the BAT table entry with the new file offset, and the new entry + * state */ +static void vhdx_update_bat_table_entry(BlockDriverState *bs, BDRVVHDXState *s, + VHDXSectorInfo *sinfo, + uint64_t *bat_entry_le, + uint64_t *bat_offset, int state) +{ + /* The BAT entry is a uint64, with 44 bits for the file offset in units of + * 1MB, and 3 bits for the block state. */ + s->bat[sinfo->bat_idx] = sinfo->file_offset; + + s->bat[sinfo->bat_idx] |= state & VHDX_BAT_STATE_BIT_MASK; + + *bat_entry_le = cpu_to_le64(s->bat[sinfo->bat_idx]); + *bat_offset = s->bat_offset + sinfo->bat_idx * sizeof(VHDXBatEntry); + +} +/* Per the spec, on the first write of guest-visible data to the file the + * data write guid must be updated in the header */ +int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s) +{ + int ret = 0; + if (s->first_visible_write) { + s->first_visible_write = false; + ret = vhdx_update_headers(bs, s, true, NULL); + } + return ret; +} static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { - return -ENOTSUP; + int ret = -ENOTSUP; + BDRVVHDXState *s = bs->opaque; + VHDXSectorInfo sinfo; + uint64_t bytes_done = 0; + uint64_t bat_entry = 0; + uint64_t bat_entry_offset = 0; + QEMUIOVector hd_qiov; + struct iovec iov1 = { 0 }; + struct iovec iov2 = { 0 }; + int sectors_to_write; + int bat_state; + uint64_t bat_prior_offset = 0; + bool bat_update = false; + + qemu_iovec_init(&hd_qiov, qiov->niov); + + qemu_co_mutex_lock(&s->lock); + + ret = vhdx_user_visible_write(bs, s); + if (ret < 0) { + goto exit; + } + + while (nb_sectors > 0) { + bool use_zero_buffers = false; + bat_update = false; + if (s->params.data_bits & VHDX_PARAMS_HAS_PARENT) { + /* not supported yet */ + ret = -ENOTSUP; + goto exit; + } else { + vhdx_block_translate(s, sector_num, nb_sectors, &sinfo); + sectors_to_write = sinfo.sectors_avail; + + qemu_iovec_reset(&hd_qiov); + /* check the payload block state */ + bat_state = s->bat[sinfo.bat_idx] & VHDX_BAT_STATE_BIT_MASK; + switch (bat_state) { + case PAYLOAD_BLOCK_ZERO: + /* in this case, we need to preserve zero writes for + * data that is not part of this write, so we must pad + * the rest of the buffer to zeroes */ + + /* if we are on a posix system with ftruncate() that extends + * a file, then it is zero-filled for us. On Win32, the raw + * layer uses SetFilePointer and SetFileEnd, which does not + * zero fill AFAIK */ + + /* Queue another write of zero buffers if the underlying file + * does not zero-fill on file extension */ + + if (bdrv_has_zero_init(bs->file) == 0) { + use_zero_buffers = true; + + /* zero fill the front, if any */ + if (sinfo.block_offset) { + iov1.iov_len = sinfo.block_offset; + iov1.iov_base = qemu_blockalign(bs, iov1.iov_len); + memset(iov1.iov_base, 0, iov1.iov_len); + qemu_iovec_concat_iov(&hd_qiov, &iov1, 1, 0, + sinfo.block_offset); + sectors_to_write += iov1.iov_len >> BDRV_SECTOR_BITS; + } + + /* our actual data */ + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, + sinfo.bytes_avail); + + /* zero fill the back, if any */ + if ((sinfo.bytes_avail - sinfo.block_offset) < + s->block_size) { + iov2.iov_len = s->block_size - + (sinfo.bytes_avail + sinfo.block_offset); + iov2.iov_base = qemu_blockalign(bs, iov2.iov_len); + memset(iov2.iov_base, 0, iov2.iov_len); + qemu_iovec_concat_iov(&hd_qiov, &iov2, 1, 0, + sinfo.block_offset); + sectors_to_write += iov2.iov_len >> BDRV_SECTOR_BITS; + } + } + + /* fall through */ + case PAYLOAD_BLOCK_NOT_PRESENT: /* fall through */ + case PAYLOAD_BLOCK_UNMAPPED: /* fall through */ + case PAYLOAD_BLOCK_UNDEFINED: /* fall through */ + bat_prior_offset = sinfo.file_offset; + ret = vhdx_allocate_block(bs, s, &sinfo.file_offset); + if (ret < 0) { + goto exit; + } + /* once we support differencing files, this may also be + * partially present */ + /* update block state to the newly specified state */ + vhdx_update_bat_table_entry(bs, s, &sinfo, &bat_entry, + &bat_entry_offset, + PAYLOAD_BLOCK_FULLY_PRESENT); + bat_update = true; + /* since we just allocated a block, file_offset is the + * beginning of the payload block. It needs to be the + * write address, which includes the offset into the block */ + if (!use_zero_buffers) { + sinfo.file_offset += sinfo.block_offset; + } + /* fall through */ + case PAYLOAD_BLOCK_FULLY_PRESENT: + /* if the file offset address is in the header zone, + * there is a problem */ + if (sinfo.file_offset < (1024 * 1024)) { + ret = -EFAULT; + goto error_bat_restore; + } + + if (!use_zero_buffers) { + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, + sinfo.bytes_avail); + } + /* block exists, so we can just overwrite it */ + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_writev(bs->file, + sinfo.file_offset >> BDRV_SECTOR_BITS, + sectors_to_write, &hd_qiov); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto error_bat_restore; + } + break; + case PAYLOAD_BLOCK_PARTIALLY_PRESENT: + /* we don't yet support difference files, fall through + * to error */ + default: + ret = -EIO; + goto exit; + break; + } + + if (bat_update) { + /* this will update the BAT entry into the log journal, and + * then flush the log journal out to disk */ + ret = vhdx_log_write_and_flush(bs, s, &bat_entry, + sizeof(VHDXBatEntry), + bat_entry_offset); + if (ret < 0) { + goto exit; + } + } + + nb_sectors -= sinfo.sectors_avail; + sector_num += sinfo.sectors_avail; + bytes_done += sinfo.bytes_avail; + + } + } + + goto exit; + +error_bat_restore: + if (bat_update) { + /* keep metadata in sync, and restore the bat entry state + * if error. */ + sinfo.file_offset = bat_prior_offset; + vhdx_update_bat_table_entry(bs, s, &sinfo, &bat_entry, + &bat_entry_offset, bat_state); + } +exit: + qemu_vfree(iov1.iov_base); + qemu_vfree(iov2.iov_base); + qemu_co_mutex_unlock(&s->lock); + qemu_iovec_destroy(&hd_qiov); + return ret; } -static void vhdx_close(BlockDriverState *bs) + +/* + * Create VHDX Headers + * + * There are 2 headers, and the highest sequence number will represent + * the active header + */ +static int vhdx_create_new_headers(BlockDriverState *bs, uint64_t image_size, + uint32_t log_size) { - BDRVVHDXState *s = bs->opaque; - qemu_vfree(s->headers[0]); - qemu_vfree(s->headers[1]); - qemu_vfree(s->bat); - qemu_vfree(s->parent_entries); - migrate_del_blocker(s->migration_blocker); - error_free(s->migration_blocker); + int ret = 0; + VHDXHeader *hdr = NULL; + + hdr = g_malloc0(sizeof(VHDXHeader)); + + hdr->signature = VHDX_HEADER_SIGNATURE; + hdr->sequence_number = g_random_int(); + hdr->log_version = 0; + hdr->version = 1; + hdr->log_length = log_size; + hdr->log_offset = VHDX_HEADER_SECTION_END; + vhdx_guid_generate(&hdr->file_write_guid); + vhdx_guid_generate(&hdr->data_write_guid); + + ret = vhdx_write_header(bs, hdr, VHDX_HEADER1_OFFSET, false); + if (ret < 0) { + goto exit; + } + hdr->sequence_number++; + ret = vhdx_write_header(bs, hdr, VHDX_HEADER2_OFFSET, false); + if (ret < 0) { + goto exit; + } + +exit: + g_free(hdr); + return ret; +} + + +/* + * Create the Metadata entries. + * + * For more details on the entries, see section 3.5 (pg 29) in the + * VHDX 1.00 specification. + * + * We support 5 metadata entries (all required by spec): + * File Parameters, + * Virtual Disk Size, + * Page 83 Data, + * Logical Sector Size, + * Physical Sector Size + * + * The first 64KB of the Metadata section is reserved for the metadata + * header and entries; beyond that, the metadata items themselves reside. + */ +static int vhdx_create_new_metadata(BlockDriverState *bs, + uint64_t image_size, + uint32_t block_size, + uint32_t sector_size, + uint64_t metadata_offset, + VHDXImageType type) +{ + int ret = 0; + uint32_t offset = 0; + void *buffer = NULL; + void *entry_buffer; + VHDXMetadataTableHeader *md_table;; + VHDXMetadataTableEntry *md_table_entry; + + /* Metadata entries */ + VHDXFileParameters *mt_file_params; + VHDXVirtualDiskSize *mt_virtual_size; + VHDXPage83Data *mt_page83; + VHDXVirtualDiskLogicalSectorSize *mt_log_sector_size; + VHDXVirtualDiskPhysicalSectorSize *mt_phys_sector_size; + + entry_buffer = g_malloc0(sizeof(VHDXFileParameters) + + sizeof(VHDXVirtualDiskSize) + + sizeof(VHDXPage83Data) + + sizeof(VHDXVirtualDiskLogicalSectorSize) + + sizeof(VHDXVirtualDiskPhysicalSectorSize)); + + mt_file_params = entry_buffer; + offset += sizeof(VHDXFileParameters); + mt_virtual_size = entry_buffer + offset; + offset += sizeof(VHDXVirtualDiskSize); + mt_page83 = entry_buffer + offset; + offset += sizeof(VHDXPage83Data); + mt_log_sector_size = entry_buffer + offset; + offset += sizeof(VHDXVirtualDiskLogicalSectorSize); + mt_phys_sector_size = entry_buffer + offset; + + mt_file_params->block_size = cpu_to_le32(block_size); + if (type == VHDX_TYPE_FIXED) { + mt_file_params->data_bits |= VHDX_PARAMS_LEAVE_BLOCKS_ALLOCED; + cpu_to_le32s(&mt_file_params->data_bits); + } + + vhdx_guid_generate(&mt_page83->page_83_data); + cpu_to_leguids(&mt_page83->page_83_data); + mt_virtual_size->virtual_disk_size = cpu_to_le64(image_size); + mt_log_sector_size->logical_sector_size = cpu_to_le32(sector_size); + mt_phys_sector_size->physical_sector_size = cpu_to_le32(sector_size); + + buffer = g_malloc0(VHDX_HEADER_BLOCK_SIZE); + md_table = buffer; + + md_table->signature = VHDX_METADATA_SIGNATURE; + md_table->entry_count = 5; + vhdx_metadata_header_le_export(md_table); + + + /* This will reference beyond the reserved table portion */ + offset = 64 * KiB; + + md_table_entry = buffer + sizeof(VHDXMetadataTableHeader); + + md_table_entry[0].item_id = file_param_guid; + md_table_entry[0].offset = offset; + md_table_entry[0].length = sizeof(VHDXFileParameters); + md_table_entry[0].data_bits |= VHDX_META_FLAGS_IS_REQUIRED; + offset += md_table_entry[0].length; + vhdx_metadata_entry_le_export(&md_table_entry[0]); + + md_table_entry[1].item_id = virtual_size_guid; + md_table_entry[1].offset = offset; + md_table_entry[1].length = sizeof(VHDXVirtualDiskSize); + md_table_entry[1].data_bits |= VHDX_META_FLAGS_IS_REQUIRED | + VHDX_META_FLAGS_IS_VIRTUAL_DISK; + offset += md_table_entry[1].length; + vhdx_metadata_entry_le_export(&md_table_entry[1]); + + md_table_entry[2].item_id = page83_guid; + md_table_entry[2].offset = offset; + md_table_entry[2].length = sizeof(VHDXPage83Data); + md_table_entry[2].data_bits |= VHDX_META_FLAGS_IS_REQUIRED | + VHDX_META_FLAGS_IS_VIRTUAL_DISK; + offset += md_table_entry[2].length; + vhdx_metadata_entry_le_export(&md_table_entry[2]); + + md_table_entry[3].item_id = logical_sector_guid; + md_table_entry[3].offset = offset; + md_table_entry[3].length = sizeof(VHDXVirtualDiskLogicalSectorSize); + md_table_entry[3].data_bits |= VHDX_META_FLAGS_IS_REQUIRED | + VHDX_META_FLAGS_IS_VIRTUAL_DISK; + offset += md_table_entry[3].length; + vhdx_metadata_entry_le_export(&md_table_entry[3]); + + md_table_entry[4].item_id = phys_sector_guid; + md_table_entry[4].offset = offset; + md_table_entry[4].length = sizeof(VHDXVirtualDiskPhysicalSectorSize); + md_table_entry[4].data_bits |= VHDX_META_FLAGS_IS_REQUIRED | + VHDX_META_FLAGS_IS_VIRTUAL_DISK; + vhdx_metadata_entry_le_export(&md_table_entry[4]); + + ret = bdrv_pwrite(bs, metadata_offset, buffer, VHDX_HEADER_BLOCK_SIZE); + if (ret < 0) { + goto exit; + } + + ret = bdrv_pwrite(bs, metadata_offset + (64 * KiB), entry_buffer, + VHDX_HEADER_BLOCK_SIZE); + if (ret < 0) { + goto exit; + } + + +exit: + g_free(buffer); + g_free(entry_buffer); + return ret; } +/* This create the actual BAT itself. We currently only support + * 'Dynamic' and 'Fixed' image types. + * + * Dynamic images: default state of the BAT is all zeroes. + * + * Fixed images: default state of the BAT is fully populated, with + * file offsets and state PAYLOAD_BLOCK_FULLY_PRESENT. + */ +static int vhdx_create_bat(BlockDriverState *bs, BDRVVHDXState *s, + uint64_t image_size, VHDXImageType type, + bool use_zero_blocks, VHDXRegionTableEntry *rt_bat) +{ + int ret = 0; + uint64_t data_file_offset; + uint64_t total_sectors = 0; + uint64_t sector_num = 0; + uint64_t unused; + int block_state; + VHDXSectorInfo sinfo; + + assert(s->bat == NULL); + + /* this gives a data start after BAT/bitmap entries, and well + * past any metadata entries (with a 4 MB buffer for future + * expansion */ + data_file_offset = rt_bat->file_offset + rt_bat->length + 5 * MiB; + total_sectors = image_size >> s->logical_sector_size_bits; + + if (type == VHDX_TYPE_DYNAMIC) { + /* All zeroes, so we can just extend the file - the end of the BAT + * is the furthest thing we have written yet */ + ret = bdrv_truncate(bs, data_file_offset); + if (ret < 0) { + goto exit; + } + } else if (type == VHDX_TYPE_FIXED) { + ret = bdrv_truncate(bs, data_file_offset + image_size); + if (ret < 0) { + goto exit; + } + } else { + ret = -ENOTSUP; + goto exit; + } + + if (type == VHDX_TYPE_FIXED || + use_zero_blocks || + bdrv_has_zero_init(bs) == 0) { + /* for a fixed file, the default BAT entry is not zero */ + s->bat = g_malloc0(rt_bat->length); + block_state = type == VHDX_TYPE_FIXED ? PAYLOAD_BLOCK_FULLY_PRESENT : + PAYLOAD_BLOCK_NOT_PRESENT; + block_state = use_zero_blocks ? PAYLOAD_BLOCK_ZERO : block_state; + /* fill the BAT by emulating sector writes of sectors_per_block size */ + while (sector_num < total_sectors) { + vhdx_block_translate(s, sector_num, s->sectors_per_block, &sinfo); + sinfo.file_offset = data_file_offset + + (sector_num << s->logical_sector_size_bits); + sinfo.file_offset = ROUND_UP(sinfo.file_offset, MiB); + vhdx_update_bat_table_entry(bs, s, &sinfo, &unused, &unused, + block_state); + cpu_to_le64s(&s->bat[sinfo.bat_idx]); + sector_num += s->sectors_per_block; + } + ret = bdrv_pwrite(bs, rt_bat->file_offset, s->bat, rt_bat->length); + if (ret < 0) { + goto exit; + } + } + + + +exit: + g_free(s->bat); + return ret; +} + +/* Creates the region table header, and region table entries. + * There are 2 supported region table entries: BAT, and Metadata/ + * + * As the calculations for the BAT region table are also needed + * to create the BAT itself, we will also cause the BAT to be + * created. + */ +static int vhdx_create_new_region_table(BlockDriverState *bs, + uint64_t image_size, + uint32_t block_size, + uint32_t sector_size, + uint32_t log_size, + bool use_zero_blocks, + VHDXImageType type, + uint64_t *metadata_offset) +{ + int ret = 0; + uint32_t offset = 0; + void *buffer = NULL; + BDRVVHDXState *s = NULL; + VHDXRegionTableHeader *region_table; + VHDXRegionTableEntry *rt_bat; + VHDXRegionTableEntry *rt_metadata; + + assert(metadata_offset != NULL); + + /* Populate enough of the BDRVVHDXState to be able to use the + * pre-existing BAT calculation, translation, and update functions */ + s = g_malloc0(sizeof(BDRVVHDXState)); + + s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) * + (uint64_t) sector_size / (uint64_t) block_size; + + s->sectors_per_block = block_size / sector_size; + s->virtual_disk_size = image_size; + s->block_size = block_size; + s->logical_sector_size = sector_size; + + vhdx_set_shift_bits(s); + + vhdx_calc_bat_entries(s); + + /* At this point the VHDX state is populated enough for creation */ + + /* a single buffer is used so we can calculate the checksum over the + * entire 64KB block */ + buffer = g_malloc0(VHDX_HEADER_BLOCK_SIZE); + region_table = buffer; + offset += sizeof(VHDXRegionTableHeader); + rt_bat = buffer + offset; + offset += sizeof(VHDXRegionTableEntry); + rt_metadata = buffer + offset; + + region_table->signature = VHDX_REGION_SIGNATURE; + region_table->entry_count = 2; /* BAT and Metadata */ + + rt_bat->guid = bat_guid; + rt_bat->length = ROUND_UP(s->bat_entries * sizeof(VHDXBatEntry), MiB); + rt_bat->file_offset = ROUND_UP(VHDX_HEADER_SECTION_END + log_size, MiB); + s->bat_offset = rt_bat->file_offset; + + rt_metadata->guid = metadata_guid; + rt_metadata->file_offset = ROUND_UP(rt_bat->file_offset + rt_bat->length, + MiB); + rt_metadata->length = 1 * MiB; /* min size, and more than enough */ + *metadata_offset = rt_metadata->file_offset; + + vhdx_update_checksum(buffer, VHDX_HEADER_BLOCK_SIZE, + offsetof(VHDXRegionTableHeader, checksum)); + + + /* The region table gives us the data we need to create the BAT, + * so do that now */ + ret = vhdx_create_bat(bs, s, image_size, type, use_zero_blocks, rt_bat); + + /* Now write out the region headers to disk */ + vhdx_region_header_le_export(region_table); + vhdx_region_entry_le_export(rt_bat); + vhdx_region_entry_le_export(rt_metadata); + + ret = bdrv_pwrite(bs, VHDX_REGION_TABLE_OFFSET, buffer, + VHDX_HEADER_BLOCK_SIZE); + if (ret < 0) { + goto exit; + } + + ret = bdrv_pwrite(bs, VHDX_REGION_TABLE2_OFFSET, buffer, + VHDX_HEADER_BLOCK_SIZE); + if (ret < 0) { + goto exit; + } + + +exit: + g_free(s); + g_free(buffer); + return ret; +} + +/* We need to create the following elements: + * + * .-----------------------------------------------------------------. + * | (A) | (B) | (C) | (D) | (E) | + * | File ID | Header1 | Header 2 | Region Tbl 1 | Region Tbl 2 | + * | | | | | | + * .-----------------------------------------------------------------. + * 0 64KB 128KB 192KB 256KB 320KB + * + * + * .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------. + * | (F) | (G) | (H) | | + * | Journal Log | BAT / Bitmap | Metadata | .... data ...... | + * | | | | | + * .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------. + * 1MB + */ +static int vhdx_create(const char *filename, QEMUOptionParameter *options, + Error **errp) +{ + int ret = 0; + uint64_t image_size = (uint64_t) 2 * GiB; + uint32_t log_size = 1 * MiB; + uint32_t block_size = 0; + uint64_t signature; + uint64_t metadata_offset; + bool use_zero_blocks = false; + + gunichar2 *creator = NULL; + glong creator_items; + BlockDriverState *bs; + const char *type = NULL; + VHDXImageType image_type; + Error *local_err = NULL; + + while (options && options->name) { + if (!strcmp(options->name, BLOCK_OPT_SIZE)) { + image_size = options->value.n; + } else if (!strcmp(options->name, VHDX_BLOCK_OPT_LOG_SIZE)) { + log_size = options->value.n; + } else if (!strcmp(options->name, VHDX_BLOCK_OPT_BLOCK_SIZE)) { + block_size = options->value.n; + } else if (!strcmp(options->name, BLOCK_OPT_SUBFMT)) { + type = options->value.s; + } else if (!strcmp(options->name, VHDX_BLOCK_OPT_ZERO)) { + use_zero_blocks = options->value.n != 0; + } + options++; + } + + if (image_size > VHDX_MAX_IMAGE_SIZE) { + error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB"); + ret = -EINVAL; + goto exit; + } + + if (type == NULL) { + type = "dynamic"; + } + + if (!strcmp(type, "dynamic")) { + image_type = VHDX_TYPE_DYNAMIC; + } else if (!strcmp(type, "fixed")) { + image_type = VHDX_TYPE_FIXED; + } else if (!strcmp(type, "differencing")) { + error_setg_errno(errp, ENOTSUP, + "Differencing files not yet supported"); + ret = -ENOTSUP; + goto exit; + } else { + ret = -EINVAL; + goto exit; + } + + /* These are pretty arbitrary, and mainly designed to keep the BAT + * size reasonable to load into RAM */ + if (block_size == 0) { + if (image_size > 32 * TiB) { + block_size = 64 * MiB; + } else if (image_size > (uint64_t) 100 * GiB) { + block_size = 32 * MiB; + } else if (image_size > 1 * GiB) { + block_size = 16 * MiB; + } else { + block_size = 8 * MiB; + } + } + + + /* make the log size close to what was specified, but must be + * min 1MB, and multiple of 1MB */ + log_size = ROUND_UP(log_size, MiB); + + block_size = ROUND_UP(block_size, MiB); + block_size = block_size > VHDX_BLOCK_SIZE_MAX ? VHDX_BLOCK_SIZE_MAX : + block_size; + + ret = bdrv_create_file(filename, options, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + + ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + + /* Create (A) */ + + /* The creator field is optional, but may be useful for + * debugging / diagnostics */ + creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL, + &creator_items, NULL); + signature = cpu_to_le64(VHDX_FILE_SIGNATURE); + bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET, &signature, sizeof(signature)); + if (ret < 0) { + goto delete_and_exit; + } + if (creator) { + bdrv_pwrite(bs, VHDX_FILE_ID_OFFSET + sizeof(signature), creator, + creator_items * sizeof(gunichar2)); + if (ret < 0) { + goto delete_and_exit; + } + } + + + /* Creates (B),(C) */ + ret = vhdx_create_new_headers(bs, image_size, log_size); + if (ret < 0) { + goto delete_and_exit; + } + + /* Creates (D),(E),(G) explicitly. (F) created as by-product */ + ret = vhdx_create_new_region_table(bs, image_size, block_size, 512, + log_size, use_zero_blocks, image_type, + &metadata_offset); + if (ret < 0) { + goto delete_and_exit; + } + + /* Creates (H) */ + ret = vhdx_create_new_metadata(bs, image_size, block_size, 512, + metadata_offset, image_type); + if (ret < 0) { + goto delete_and_exit; + } + + + +delete_and_exit: + bdrv_unref(bs); +exit: + g_free(creator); + return ret; +} + +static QEMUOptionParameter vhdx_create_options[] = { + { + .name = BLOCK_OPT_SIZE, + .type = OPT_SIZE, + .help = "Virtual disk size; max of 64TB." + }, + { + .name = VHDX_BLOCK_OPT_LOG_SIZE, + .type = OPT_SIZE, + .value.n = 1 * MiB, + .help = "Log size; min 1MB." + }, + { + .name = VHDX_BLOCK_OPT_BLOCK_SIZE, + .type = OPT_SIZE, + .value.n = 0, + .help = "Block Size; min 1MB, max 256MB. " \ + "0 means auto-calculate based on image size." + }, + { + .name = BLOCK_OPT_SUBFMT, + .type = OPT_STRING, + .help = "VHDX format type, can be either 'dynamic' or 'fixed'. "\ + "Default is 'dynamic'." + }, + { + .name = VHDX_BLOCK_OPT_ZERO, + .type = OPT_FLAG, + .help = "Force use of payload blocks of type 'ZERO'. Non-standard." + }, + { NULL } +}; + static BlockDriver bdrv_vhdx = { .format_name = "vhdx", .instance_size = sizeof(BDRVVHDXState), @@ -973,6 +1884,9 @@ static BlockDriver bdrv_vhdx = { .bdrv_reopen_prepare = vhdx_reopen_prepare, .bdrv_co_readv = vhdx_co_readv, .bdrv_co_writev = vhdx_co_writev, + .bdrv_create = vhdx_create, + + .create_options = vhdx_create_options, }; static void bdrv_vhdx_init(void) diff --git a/block/vhdx.h b/block/vhdx.h index fb687ed2d6..51183b243c 100644 --- a/block/vhdx.h +++ b/block/vhdx.h @@ -6,9 +6,9 @@ * Authors: * Jeff Cody <jcody@redhat.com> * - * This is based on the "VHDX Format Specification v0.95", published 4/12/2012 + * This is based on the "VHDX Format Specification v1.00", published 8/25/2012 * by Microsoft: - * https://www.microsoft.com/en-us/download/details.aspx?id=29681 + * https://www.microsoft.com/en-us/download/details.aspx?id=34750 * * 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. @@ -18,6 +18,11 @@ #ifndef BLOCK_VHDX_H #define BLOCK_VHDX_H +#define KiB (1 * 1024) +#define MiB (KiB * 1024) +#define GiB (MiB * 1024) +#define TiB ((uint64_t) GiB * 1024) + /* Structures and fields present in the VHDX file */ /* The header section has the following blocks, @@ -30,14 +35,15 @@ * 0.........64KB...........128KB........192KB..........256KB................1MB */ -#define VHDX_HEADER_BLOCK_SIZE (64*1024) +#define VHDX_HEADER_BLOCK_SIZE (64 * 1024) #define VHDX_FILE_ID_OFFSET 0 -#define VHDX_HEADER1_OFFSET (VHDX_HEADER_BLOCK_SIZE*1) -#define VHDX_HEADER2_OFFSET (VHDX_HEADER_BLOCK_SIZE*2) -#define VHDX_REGION_TABLE_OFFSET (VHDX_HEADER_BLOCK_SIZE*3) - +#define VHDX_HEADER1_OFFSET (VHDX_HEADER_BLOCK_SIZE * 1) +#define VHDX_HEADER2_OFFSET (VHDX_HEADER_BLOCK_SIZE * 2) +#define VHDX_REGION_TABLE_OFFSET (VHDX_HEADER_BLOCK_SIZE * 3) +#define VHDX_REGION_TABLE2_OFFSET (VHDX_HEADER_BLOCK_SIZE * 4) +#define VHDX_HEADER_SECTION_END (1 * MiB) /* * A note on the use of MS-GUID fields. For more details on the GUID, * please see: https://en.wikipedia.org/wiki/Globally_unique_identifier. @@ -55,10 +61,11 @@ /* These structures are ones that are defined in the VHDX specification * document */ +#define VHDX_FILE_SIGNATURE 0x656C696678646876 /* "vhdxfile" in ASCII */ typedef struct VHDXFileIdentifier { uint64_t signature; /* "vhdxfile" in ASCII */ uint16_t creator[256]; /* optional; utf-16 string to identify - the vhdx file creator. Diagnotistic + the vhdx file creator. Diagnostic only */ } VHDXFileIdentifier; @@ -67,7 +74,7 @@ typedef struct VHDXFileIdentifier { * Microsoft is not just 16 bytes though - it is a structure that is defined, * so we need to follow it here so that endianness does not trip us up */ -typedef struct MSGUID { +typedef struct QEMU_PACKED MSGUID { uint32_t data1; uint16_t data2; uint16_t data3; @@ -77,14 +84,15 @@ typedef struct MSGUID { #define guid_eq(a, b) \ (memcmp(&(a), &(b), sizeof(MSGUID)) == 0) -#define VHDX_HEADER_SIZE (4*1024) /* although the vhdx_header struct in disk - is only 582 bytes, for purposes of crc - the header is the first 4KB of the 64KB - block */ +#define VHDX_HEADER_SIZE (4 * 1024) /* although the vhdx_header struct in disk + is only 582 bytes, for purposes of crc + the header is the first 4KB of the 64KB + block */ /* The full header is 4KB, although the actual header data is much smaller. * But for the checksum calculation, it is over the entire 4KB structure, * not just the defined portion of it */ +#define VHDX_HEADER_SIGNATURE 0x64616568 typedef struct QEMU_PACKED VHDXHeader { uint32_t signature; /* "head" in ASCII */ uint32_t checksum; /* CRC-32C hash of the whole header */ @@ -92,7 +100,7 @@ typedef struct QEMU_PACKED VHDXHeader { VHDX file has 2 of these headers, and only the header with the highest sequence number is valid */ - MSGUID file_write_guid; /* 128 bit unique identifier. Must be + MSGUID file_write_guid; /* 128 bit unique identifier. Must be updated to new, unique value before the first modification is made to file */ @@ -114,9 +122,9 @@ typedef struct QEMU_PACKED VHDXHeader { there is no valid log. If non-zero, log entries with this guid are valid. */ - uint16_t log_version; /* version of the log format. Mustn't be - zero, unless log_guid is also zero */ - uint16_t version; /* version of th evhdx file. Currently, + uint16_t log_version; /* version of the log format. Must be + set to zero */ + uint16_t version; /* version of the vhdx file. Currently, only supported version is "1" */ uint32_t log_length; /* length of the log. Must be multiple of 1MB */ @@ -125,6 +133,7 @@ typedef struct QEMU_PACKED VHDXHeader { } VHDXHeader; /* Header for the region table block */ +#define VHDX_REGION_SIGNATURE 0x69676572 /* "regi" in ASCII */ typedef struct QEMU_PACKED VHDXRegionTableHeader { uint32_t signature; /* "regi" in ASCII */ uint32_t checksum; /* CRC-32C hash of the 64KB table */ @@ -151,7 +160,10 @@ typedef struct QEMU_PACKED VHDXRegionTableEntry { /* ---- LOG ENTRY STRUCTURES ---- */ +#define VHDX_LOG_MIN_SIZE (1024 * 1024) +#define VHDX_LOG_SECTOR_SIZE 4096 #define VHDX_LOG_HDR_SIZE 64 +#define VHDX_LOG_SIGNATURE 0x65676f6c typedef struct QEMU_PACKED VHDXLogEntryHeader { uint32_t signature; /* "loge" in ASCII */ uint32_t checksum; /* CRC-32C hash of the 64KB table */ @@ -174,7 +186,8 @@ typedef struct QEMU_PACKED VHDXLogEntryHeader { } VHDXLogEntryHeader; #define VHDX_LOG_DESC_SIZE 32 - +#define VHDX_LOG_DESC_SIGNATURE 0x63736564 +#define VHDX_LOG_ZERO_SIGNATURE 0x6f72657a typedef struct QEMU_PACKED VHDXLogDescriptor { uint32_t signature; /* "zero" or "desc" in ASCII */ union { @@ -194,6 +207,7 @@ typedef struct QEMU_PACKED VHDXLogDescriptor { vhdx_log_entry_header */ } VHDXLogDescriptor; +#define VHDX_LOG_DATA_SIGNATURE 0x61746164 typedef struct QEMU_PACKED VHDXLogDataSector { uint32_t data_signature; /* "data" in ASCII */ uint32_t sequence_high; /* 4 MSB of 8 byte sequence_number */ @@ -212,19 +226,19 @@ typedef struct QEMU_PACKED VHDXLogDataSector { #define PAYLOAD_BLOCK_UNDEFINED 1 #define PAYLOAD_BLOCK_ZERO 2 #define PAYLOAD_BLOCK_UNMAPPED 5 -#define PAYLOAD_BLOCK_FULL_PRESENT 6 +#define PAYLOAD_BLOCK_FULLY_PRESENT 6 #define PAYLOAD_BLOCK_PARTIALLY_PRESENT 7 #define SB_BLOCK_NOT_PRESENT 0 #define SB_BLOCK_PRESENT 6 /* per the spec */ -#define VHDX_MAX_SECTORS_PER_BLOCK (1<<23) +#define VHDX_MAX_SECTORS_PER_BLOCK (1 << 23) /* upper 44 bits are the file offset in 1MB units lower 3 bits are the state other bits are reserved */ #define VHDX_BAT_STATE_BIT_MASK 0x07 -#define VHDX_BAT_FILE_OFF_BITS (64-44) +#define VHDX_BAT_FILE_OFF_MASK 0xFFFFFFFFFFF00000 /* upper 44 bits */ typedef uint64_t VHDXBatEntry; /* ---- METADATA REGION STRUCTURES ---- */ @@ -233,6 +247,7 @@ typedef uint64_t VHDXBatEntry; #define VHDX_METADATA_MAX_ENTRIES 2047 /* not including the header */ #define VHDX_METADATA_TABLE_MAX_SIZE \ (VHDX_METADATA_ENTRY_SIZE * (VHDX_METADATA_MAX_ENTRIES+1)) +#define VHDX_METADATA_SIGNATURE 0x617461646174656D /* "metadata" in ASCII */ typedef struct QEMU_PACKED VHDXMetadataTableHeader { uint64_t signature; /* "metadata" in ASCII */ uint16_t reserved; @@ -252,8 +267,8 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry { metadata region */ /* note: if length = 0, so is offset */ uint32_t length; /* length of metadata. <= 1MB. */ - uint32_t data_bits; /* least-significant 3 bits are flags, the - rest are reserved (see above) */ + uint32_t data_bits; /* least-significant 3 bits are flags, + the rest are reserved (see above) */ uint32_t reserved2; } VHDXMetadataTableEntry; @@ -262,13 +277,16 @@ typedef struct QEMU_PACKED VHDXMetadataTableEntry { If set indicates a fixed size VHDX file */ #define VHDX_PARAMS_HAS_PARENT 0x02 /* has parent / backing file */ +#define VHDX_BLOCK_SIZE_MIN (1 * MiB) +#define VHDX_BLOCK_SIZE_MAX (256 * MiB) typedef struct QEMU_PACKED VHDXFileParameters { uint32_t block_size; /* size of each payload block, always power of 2, <= 256MB and >= 1MB. */ - uint32_t data_bits; /* least-significant 2 bits are flags, the rest - are reserved (see above) */ + uint32_t data_bits; /* least-significant 2 bits are flags, + the rest are reserved (see above) */ } VHDXFileParameters; +#define VHDX_MAX_IMAGE_SIZE ((uint64_t) 64 * TiB) typedef struct QEMU_PACKED VHDXVirtualDiskSize { uint64_t virtual_disk_size; /* Size of the virtual disk, in bytes. Must be multiple of the sector size, @@ -276,7 +294,7 @@ typedef struct QEMU_PACKED VHDXVirtualDiskSize { } VHDXVirtualDiskSize; typedef struct QEMU_PACKED VHDXPage83Data { - MSGUID page_83_data[16]; /* unique id for scsi devices that + MSGUID page_83_data; /* unique id for scsi devices that support page 0x83 */ } VHDXPage83Data; @@ -291,7 +309,7 @@ typedef struct QEMU_PACKED VHDXVirtualDiskPhysicalSectorSize { } VHDXVirtualDiskPhysicalSectorSize; typedef struct QEMU_PACKED VHDXParentLocatorHeader { - MSGUID locator_type[16]; /* type of the parent virtual disk. */ + MSGUID locator_type; /* type of the parent virtual disk. */ uint16_t reserved; uint16_t key_value_count; /* number of key/value pairs for this locator */ @@ -308,18 +326,122 @@ typedef struct QEMU_PACKED VHDXParentLocatorEntry { /* ----- END VHDX SPECIFICATION STRUCTURES ---- */ +typedef struct VHDXMetadataEntries { + VHDXMetadataTableEntry file_parameters_entry; + VHDXMetadataTableEntry virtual_disk_size_entry; + VHDXMetadataTableEntry page83_data_entry; + VHDXMetadataTableEntry logical_sector_size_entry; + VHDXMetadataTableEntry phys_sector_size_entry; + VHDXMetadataTableEntry parent_locator_entry; + uint16_t present; +} VHDXMetadataEntries; + +typedef struct VHDXLogEntries { + uint64_t offset; + uint64_t length; + uint32_t write; + uint32_t read; + VHDXLogEntryHeader *hdr; + void *desc_buffer; + uint64_t sequence; + uint32_t tail; +} VHDXLogEntries; + +typedef struct VHDXRegionEntry { + uint64_t start; + uint64_t end; + QLIST_ENTRY(VHDXRegionEntry) entries; +} VHDXRegionEntry; + +typedef struct BDRVVHDXState { + CoMutex lock; + + int curr_header; + VHDXHeader *headers[2]; + + VHDXRegionTableHeader rt; + VHDXRegionTableEntry bat_rt; /* region table for the BAT */ + VHDXRegionTableEntry metadata_rt; /* region table for the metadata */ + + VHDXMetadataTableHeader metadata_hdr; + VHDXMetadataEntries metadata_entries; + + VHDXFileParameters params; + uint32_t block_size; + uint32_t block_size_bits; + uint32_t sectors_per_block; + uint32_t sectors_per_block_bits; + + uint64_t virtual_disk_size; + uint32_t logical_sector_size; + uint32_t physical_sector_size; + + uint64_t chunk_ratio; + uint32_t chunk_ratio_bits; + uint32_t logical_sector_size_bits; + + uint32_t bat_entries; + VHDXBatEntry *bat; + uint64_t bat_offset; + bool first_visible_write; + MSGUID session_guid; + + VHDXLogEntries log; + + VHDXParentLocatorHeader parent_header; + VHDXParentLocatorEntry *parent_entries; + + Error *migration_blocker; + + QLIST_HEAD(VHDXRegionHead, VHDXRegionEntry) regions; +} BDRVVHDXState; + +void vhdx_guid_generate(MSGUID *guid); + +int vhdx_update_headers(BlockDriverState *bs, BDRVVHDXState *s, bool rw, + MSGUID *log_guid); + +uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset); uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, int crc_offset); bool vhdx_checksum_is_valid(uint8_t *buf, size_t size, int crc_offset); +int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed); + +int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, + void *data, uint32_t length, uint64_t offset); -static void leguid_to_cpus(MSGUID *guid) +static inline void leguid_to_cpus(MSGUID *guid) { le32_to_cpus(&guid->data1); le16_to_cpus(&guid->data2); le16_to_cpus(&guid->data3); } +static inline void cpu_to_leguids(MSGUID *guid) +{ + cpu_to_le32s(&guid->data1); + cpu_to_le16s(&guid->data2); + cpu_to_le16s(&guid->data3); +} + +void vhdx_header_le_import(VHDXHeader *h); +void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h); +void vhdx_log_desc_le_import(VHDXLogDescriptor *d); +void vhdx_log_desc_le_export(VHDXLogDescriptor *d); +void vhdx_log_data_le_export(VHDXLogDataSector *d); +void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr); +void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr); +void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr); +void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr); +void vhdx_region_entry_le_import(VHDXRegionTableEntry *e); +void vhdx_region_entry_le_export(VHDXRegionTableEntry *e); +void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr); +void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr); +void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e); +void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e); +int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s); + #endif diff --git a/block/vmdk.c b/block/vmdk.c index 32ec8b7766..a7ebd0f125 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -106,6 +106,7 @@ typedef struct VmdkExtent { uint32_t l2_cache_counts[L2_CACHE_SIZE]; int64_t cluster_sectors; + char *type; } VmdkExtent; typedef struct BDRVVmdkState { @@ -113,11 +114,13 @@ typedef struct BDRVVmdkState { uint64_t desc_offset; bool cid_updated; bool cid_checked; + uint32_t cid; uint32_t parent_cid; int num_extents; /* Extent array with num_extents entries, ascend ordered by address */ VmdkExtent *extents; Error *migration_blocker; + char *create_type; } BDRVVmdkState; typedef struct VmdkMetaData { @@ -214,6 +217,7 @@ static void vmdk_free_extents(BlockDriverState *bs) g_free(e->l1_table); g_free(e->l2_cache); g_free(e->l1_backup_table); + g_free(e->type); if (e->file != bs->file) { bdrv_unref(e->file); } @@ -534,6 +538,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, uint32_t l1_size, l1_entry_sectors; VMDK4Header header; VmdkExtent *extent; + BDRVVmdkState *s = bs->opaque; int64_t l1_backup_offset = 0; ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); @@ -549,6 +554,10 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, } } + if (!s->create_type) { + s->create_type = g_strdup("monolithicSparse"); + } + if (le64_to_cpu(header.gd_offset) == VMDK4_GD_AT_END) { /* * The footer takes precedence over the header, so read it in. The @@ -709,6 +718,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, int64_t flat_offset; char extent_path[PATH_MAX]; BlockDriverState *extent_file; + BDRVVmdkState *s = bs->opaque; + VmdkExtent *extent; while (*p) { /* parse extent line: @@ -751,7 +762,6 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, /* save to extents array */ if (!strcmp(type, "FLAT") || !strcmp(type, "VMFS")) { /* FLAT extent */ - VmdkExtent *extent; ret = vmdk_add_extent(bs, extent_file, true, sectors, 0, 0, 0, 0, 0, &extent, errp); @@ -766,10 +776,12 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, bdrv_unref(extent_file); return ret; } + extent = &s->extents[s->num_extents - 1]; } else { error_setg(errp, "Unsupported extent type '%s'", type); return -ENOTSUP; } + extent->type = g_strdup(type); next_line: /* move to next line */ while (*p) { @@ -817,6 +829,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, ret = -ENOTSUP; goto exit; } + s->create_type = g_strdup(ct); s->desc_offset = 0; ret = vmdk_parse_extents(buf, bs, bs->file->filename, errp); exit: @@ -843,6 +856,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, if (ret) { goto fail; } + s->cid = vmdk_read_cid(bs, 0); s->parent_cid = vmdk_read_cid(bs, 1); qemu_co_mutex_init(&s->lock); @@ -855,6 +869,8 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, return 0; fail: + g_free(s->create_type); + s->create_type = NULL; vmdk_free_extents(bs); return ret; } @@ -1766,6 +1782,7 @@ static void vmdk_close(BlockDriverState *bs) BDRVVmdkState *s = bs->opaque; vmdk_free_extents(bs); + g_free(s->create_type); migrate_del_blocker(s->migration_blocker); error_free(s->migration_blocker); @@ -1827,6 +1844,54 @@ static int vmdk_has_zero_init(BlockDriverState *bs) return 1; } +static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent) +{ + ImageInfo *info = g_new0(ImageInfo, 1); + + *info = (ImageInfo){ + .filename = g_strdup(extent->file->filename), + .format = g_strdup(extent->type), + .virtual_size = extent->sectors * BDRV_SECTOR_SIZE, + .compressed = extent->compressed, + .has_compressed = extent->compressed, + .cluster_size = extent->cluster_sectors * BDRV_SECTOR_SIZE, + .has_cluster_size = !extent->flat, + }; + + return info; +} + +static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs) +{ + int i; + BDRVVmdkState *s = bs->opaque; + ImageInfoSpecific *spec_info = g_new0(ImageInfoSpecific, 1); + ImageInfoList **next; + + *spec_info = (ImageInfoSpecific){ + .kind = IMAGE_INFO_SPECIFIC_KIND_VMDK, + { + .vmdk = g_new0(ImageInfoSpecificVmdk, 1), + }, + }; + + *spec_info->vmdk = (ImageInfoSpecificVmdk) { + .create_type = g_strdup(s->create_type), + .cid = s->cid, + .parent_cid = s->parent_cid, + }; + + next = &spec_info->vmdk->extents; + for (i = 0; i < s->num_extents; i++) { + *next = g_new0(ImageInfoList, 1); + (*next)->value = vmdk_get_extent_info(&s->extents[i]); + (*next)->next = NULL; + next = &(*next)->next; + } + + return spec_info; +} + static QEMUOptionParameter vmdk_create_options[] = { { .name = BLOCK_OPT_SIZE, @@ -1879,6 +1944,7 @@ static BlockDriver bdrv_vmdk = { .bdrv_co_get_block_status = vmdk_co_get_block_status, .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, .bdrv_has_zero_init = vmdk_has_zero_init, + .bdrv_get_specific_info = vmdk_get_specific_info, .create_options = vmdk_create_options, }; diff --git a/block/vpc.c b/block/vpc.c index b5dca3961e..577cc45992 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -211,6 +211,15 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, bs->total_sectors = (int64_t) be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; + /* images created with disk2vhd report a far higher virtual size + * than expected with the cyls * heads * sectors_per_cyl formula. + * use the footer->size instead if the image was created with + * disk2vhd. + */ + if (!strncmp(footer->creator_app, "d2v", 4)) { + bs->total_sectors = be64_to_cpu(footer->size) / BDRV_SECTOR_SIZE; + } + /* Allow a maximum disk size of approximately 2 TB */ if (bs->total_sectors >= 65535LL * 255 * 255) { ret = -EFBIG; @@ -260,6 +269,13 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } } + if (s->free_data_block_offset > bdrv_getlength(bs->file)) { + error_setg(errp, "block-vpc: free_data_block_offset points after " + "the end of file. The image has been truncated."); + ret = -EINVAL; + goto fail; + } + s->last_bitmap_offset = (int64_t) -1; #ifdef CACHE diff --git a/blockdev.c b/blockdev.c index b260477f1b..330aa4a3a4 100644 --- a/blockdev.c +++ b/blockdev.c @@ -47,7 +47,6 @@ #include "sysemu/arch_init.h" static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); -extern QemuOptsList qemu_common_drive_opts; static const char *const if_name[IF_COUNT] = { [IF_NONE] = "none", @@ -341,7 +340,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts, qemu_opts_absorb_qdict(opts, bs_opts, &error); if (error_is_set(&error)) { error_propagate(errp, error); - return NULL; + goto early_err; } if (id) { @@ -361,7 +360,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts, if ((buf = qemu_opt_get(opts, "discard")) != NULL) { if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) { error_setg(errp, "invalid discard option"); - return NULL; + goto early_err; } } @@ -383,7 +382,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts, /* this is the default */ } else { error_setg(errp, "invalid aio option"); - return NULL; + goto early_err; } } #endif @@ -393,13 +392,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts, error_printf("Supported formats:"); bdrv_iterate_format(bdrv_format_print, NULL); error_printf("\n"); - return NULL; + goto early_err; } drv = bdrv_find_format(buf); if (!drv) { error_setg(errp, "'%s' invalid format", buf); - return NULL; + goto early_err; } } @@ -435,20 +434,20 @@ static DriveInfo *blockdev_init(QDict *bs_opts, if (!check_throttle_config(&cfg, &error)) { error_propagate(errp, error); - return NULL; + goto early_err; } on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; if ((buf = qemu_opt_get(opts, "werror")) != NULL) { if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) { error_setg(errp, "werror is not supported by this bus type"); - return NULL; + goto early_err; } on_write_error = parse_block_error_action(buf, 0, &error); if (error_is_set(&error)) { error_propagate(errp, error); - return NULL; + goto early_err; } } @@ -456,13 +455,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts, if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) { error_report("rerror is not supported by this bus type"); - return NULL; + goto early_err; } on_read_error = parse_block_error_action(buf, 1, &error); if (error_is_set(&error)) { error_propagate(errp, error); - return NULL; + goto early_err; } } @@ -491,6 +490,8 @@ static DriveInfo *blockdev_init(QDict *bs_opts, if (has_driver_specific_opts) { file = NULL; } else { + QDECREF(bs_opts); + qemu_opts_del(opts); return dinfo; } } @@ -529,12 +530,13 @@ static DriveInfo *blockdev_init(QDict *bs_opts, return dinfo; err: - qemu_opts_del(opts); - QDECREF(bs_opts); bdrv_unref(dinfo->bdrv); g_free(dinfo->id); QTAILQ_REMOVE(&drives, dinfo, next); g_free(dinfo); +early_err: + QDECREF(bs_opts); + qemu_opts_del(opts); return NULL; } @@ -2026,7 +2028,9 @@ void qmp_drive_mirror(const char *device, const char *target, return; } - if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) { + if ((sync == MIRROR_SYNC_MODE_FULL || !source) + && mode != NEW_IMAGE_MODE_EXISTING) + { /* create new image w/o backing file */ assert(format && drv); bdrv_img_create(target, format, diff --git a/configure b/configure index 57ee62a696..508f6a574c 100755 --- a/configure +++ b/configure @@ -27,6 +27,19 @@ printf " '%s'" "$0" "$@" >> config.log echo >> config.log echo "#" >> config.log +# Save the configure command line for later reuse. +cat <<EOD >config.status +#!/bin/sh +# Generated by configure. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. +EOD +printf "exec" >>config.status +printf " '%s'" "$0" "$@" >>config.status +echo >>config.status +chmod +x config.status + error_exit() { echo echo "ERROR: $1" @@ -119,6 +132,7 @@ path_of() { # default parameters source_path=`dirname "$0"` cpu="" +iasl="iasl" interp_prefix="/usr/gnemul/qemu-%M" static="no" cross_prefix="" @@ -246,6 +260,7 @@ gtk="" gtkabi="2.0" tpm="no" libssh2="" +vhdx="" # parse CC options first for opt do @@ -257,6 +272,8 @@ for opt do ;; --cxx=*) CXX="$optarg" ;; + --iasl=*) iasl="$optarg" + ;; --source-path=*) source_path="$optarg" ;; --cpu=*) cpu="$optarg" @@ -576,7 +593,7 @@ fi : ${make=${MAKE-make}} : ${install=${INSTALL-install}} -: ${python=${PYTHON-python -B}} +: ${python=${PYTHON-python}} : ${smbd=${SMBD-/usr/sbin/smbd}} # Default objcc to clang if available, otherwise use CC @@ -969,6 +986,10 @@ for opt do ;; --enable-libssh2) libssh2="yes" ;; + --enable-vhdx) vhdx="yes" + ;; + --disable-vhdx) vhdx="no" + ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; esac @@ -1058,6 +1079,7 @@ echo "Advanced options (experts only):" echo " --source-path=PATH path of source code [$source_path]" echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]" echo " --cc=CC use C compiler CC [$cc]" +echo " --iasl=IASL use ACPI compiler IASL [$iasl]" echo " --host-cc=CC use C compiler CC [$host_cc] for code run at" echo " build time" echo " --cxx=CXX use C++ compiler CXX [$cxx]" @@ -1200,6 +1222,8 @@ echo " --gcov=GCOV use specified gcov [$gcov_tool]" echo " --enable-tpm enable TPM support" echo " --disable-libssh2 disable ssh block device support" echo " --enable-libssh2 enable ssh block device support" +echo " --disable-vhdx disables support for the Microsoft VHDX image format" +echo " --enable-vhdx enable support for the Microsoft VHDX image format" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 @@ -1396,6 +1420,13 @@ if ! $python -c 'import sys; sys.exit(sys.version_info < (2,4) or sys.version_in "Use --python=/path/to/python to specify a supported Python." fi +# The -B switch was added in Python 2.6. +# If it is supplied, compiled files are not written. +# Use it for Python versions which support it. +if $python -B -c 'import sys; sys.exit(0)' 2>/dev/null; then + python="$python -B" +fi + if test -z "${target_list+xxx}" ; then target_list="$default_target_list" else @@ -1429,39 +1460,27 @@ feature_not_found() { "configure was not able to find it" } -if test -z "$cross_prefix" ; then - # --- # big/little endian test cat > $TMPC << EOF -#include <inttypes.h> -int main(void) { - volatile uint32_t i=0x01234567; - return (*((uint8_t*)(&i))) == 0x67; +short big_endian[] = { 0x4269, 0x4765, 0x4e64, 0x4961, 0x4e00, 0, }; +short little_endian[] = { 0x694c, 0x7454, 0x654c, 0x6e45, 0x6944, 0x6e41, 0, }; +extern int foo(short *, short *); +int main(int argc, char *argv[]) { + return foo(big_endian, little_endian); } EOF -if compile_prog "" "" ; then -$TMPE && bigendian="yes" -else -echo big/little test failed -fi - -else - -# if cross compiling, cannot launch a program, so make a static guess -case "$cpu" in - arm) - # ARM can be either way; ask the compiler which one we are - if check_define __ARMEB__; then - bigendian=yes +if compile_object ; then + if grep -q BiGeNdIaN $TMPO ; then + bigendian="yes" + elif grep -q LiTtLeEnDiAn $TMPO ; then + bigendian="no" + else + echo big/little test failed fi - ;; - hppa|m68k|mips|mips64|ppc|ppc64|s390|s390x|sparc|sparc64) - bigendian=yes - ;; -esac - +else + echo big/little test failed fi ########################################## @@ -2012,6 +2031,18 @@ EOF fi fi +if test "$vhdx" = "yes" ; then + if test "$uuid" = "no" ; then + error_exit "uuid required for VHDX support" + fi +elif test "$vhdx" != "no" ; then + if test "$uuid" = "yes" ; then + vhdx=yes + else + vhdx=no + fi +fi + ########################################## # xfsctl() probe, used for raw-posix if test "$xfs" != "no" ; then @@ -3755,6 +3786,7 @@ echo "TPM support $tpm" echo "libssh2 support $libssh2" echo "TPM passthrough $tpm_passthrough" echo "QOM debugging $qom_cast_debug" +echo "vhdx $vhdx" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -3765,8 +3797,6 @@ config_host_mak="config-host.mak" echo "# Automatically generated by configure - do not modify" >config-all-disas.mak echo "# Automatically generated by configure - do not modify" > $config_host_mak -printf "# Configured with:" >> $config_host_mak -printf " '%s'" "$0" "$@" >> $config_host_mak echo >> $config_host_mak echo all: >> $config_host_mak @@ -4149,6 +4179,10 @@ if test "$virtio_blk_data_plane" = "yes" ; then echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak fi +if test "$vhdx" = "yes" ; then + echo "CONFIG_VHDX=y" >> $config_host_mak +fi + # USB host support if test "$libusb" = "yes"; then echo "HOST_USB=libusb legacy" >> $config_host_mak @@ -4234,6 +4268,9 @@ else fi echo "PYTHON=$python" >> $config_host_mak echo "CC=$cc" >> $config_host_mak +if $iasl -h > /dev/null 2>&1; then + echo "IASL=$iasl" >> $config_host_mak +fi echo "CC_I386=$cc_i386" >> $config_host_mak echo "HOST_CC=$host_cc" >> $config_host_mak echo "CXX=$cxx" >> $config_host_mak @@ -4647,6 +4684,7 @@ fi # build tree in object directory in case the source is not in the current directory DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests" +DIRS="$DIRS fsdev" DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" DIRS="$DIRS roms/seabios roms/vgabios" DIRS="$DIRS qapi-generated" @@ -4686,7 +4724,7 @@ for rom in seabios vgabios ; do echo "BCC=bcc" >> $config_mak echo "CPP=$cpp" >> $config_mak echo "OBJCOPY=objcopy" >> $config_mak - echo "IASL=iasl" >> $config_mak + echo "IASL=$iasl" >> $config_mak echo "LD=$ld" >> $config_mak done diff --git a/cpus.c b/cpus.c index 398229ecbd..01d128d7af 100644 --- a/cpus.c +++ b/cpus.c @@ -165,36 +165,38 @@ int64_t cpu_get_icount(void) /* Caller must hold the BQL */ int64_t cpu_get_ticks(void) { + int64_t ticks; + if (use_icount) { return cpu_get_icount(); } - if (!timers_state.cpu_ticks_enabled) { - return timers_state.cpu_ticks_offset; - } else { - int64_t ticks; - ticks = cpu_get_real_ticks(); - if (timers_state.cpu_ticks_prev > ticks) { - /* Note: non increasing ticks may happen if the host uses - software suspend */ - timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; - } - timers_state.cpu_ticks_prev = ticks; - return ticks + timers_state.cpu_ticks_offset; + + ticks = timers_state.cpu_ticks_offset; + if (timers_state.cpu_ticks_enabled) { + ticks += cpu_get_real_ticks(); + } + + if (timers_state.cpu_ticks_prev > ticks) { + /* Note: non increasing ticks may happen if the host uses + software suspend */ + timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; + ticks = timers_state.cpu_ticks_prev; } + + timers_state.cpu_ticks_prev = ticks; + return ticks; } static int64_t cpu_get_clock_locked(void) { - int64_t ti; + int64_t ticks; - if (!timers_state.cpu_ticks_enabled) { - ti = timers_state.cpu_clock_offset; - } else { - ti = get_clock(); - ti += timers_state.cpu_clock_offset; + ticks = timers_state.cpu_clock_offset; + if (timers_state.cpu_ticks_enabled) { + ticks += get_clock(); } - return ti; + return ticks; } /* return the host CPU monotonic timer and handle stop/restart */ @@ -235,7 +237,7 @@ void cpu_disable_ticks(void) /* Here, the really thing protected by seqlock is cpu_clock_offset. */ seqlock_write_lock(&timers_state.vm_clock_seqlock); if (timers_state.cpu_ticks_enabled) { - timers_state.cpu_ticks_offset = cpu_get_ticks(); + timers_state.cpu_ticks_offset += cpu_get_real_ticks(); timers_state.cpu_clock_offset = cpu_get_clock_locked(); timers_state.cpu_ticks_enabled = 0; } @@ -1403,7 +1405,10 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, l = sizeof(buf); if (l > size) l = size; - cpu_memory_rw_debug(cpu, addr, buf, l, 0); + if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "specified", addr); + goto exit; + } if (fwrite(buf, 1, l, f) != l) { error_set(errp, QERR_IO_ERROR); goto exit; diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index d13bc2bb22..a555eefed5 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -61,6 +61,7 @@ CONFIG_BITBANG_I2C=y CONFIG_FRAMEBUFFER=y CONFIG_XILINX_SPIPS=y +CONFIG_ARM11SCU=y CONFIG_A9SCU=y CONFIG_MARVELL_88W8618=y CONFIG_OMAP=y @@ -79,3 +80,4 @@ CONFIG_VERSATILE_PCI=y CONFIG_VERSATILE_I2C=y CONFIG_SDHCI=y +CONFIG_INTEGRATOR_DEBUG=y diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index 975112acf7..fb34a9b074 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -46,6 +46,7 @@ CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) # For pSeries CONFIG_XICS=$(CONFIG_PSERIES) +CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM)) # For PReP CONFIG_I82378=y CONFIG_I8259=y diff --git a/docs/ccid.txt b/docs/ccid.txt index 8bbaa940c3..83c174db26 100644 --- a/docs/ccid.txt +++ b/docs/ccid.txt @@ -52,7 +52,7 @@ Configuring and building: Assuming you have a working smartcard on the host with the current user, using NSS, qemu acts as another NSS client using ccid-card-emulated: - qemu -usb -device usb-ccid -device ccid-card-emualated + qemu -usb -device usb-ccid -device ccid-card-emulated 4. Using ccid-card-emulated with certificates diff --git a/docs/memory.txt b/docs/memory.txt index feb9fe90d7..22eaec780e 100644 --- a/docs/memory.txt +++ b/docs/memory.txt @@ -52,6 +52,15 @@ MemoryRegion): hole". Aliases may point to any type of region, including other aliases, but an alias may not point back to itself, directly or indirectly. +It is valid to add subregions to a region which is not a pure container +(that is, to an MMIO, RAM or ROM region). This means that the region +will act like a container, except that any addresses within the container's +region which are not claimed by any subregion are handled by the +container itself (ie by its MMIO callbacks or RAM backing). However +it is generally possible to achieve the same effect with a pure container +one of whose subregions is a low priority "background" region covering +the whole address range; this is often clearer and is preferred. +Subregions cannot be added to an alias region. Region names ------------ @@ -80,6 +89,53 @@ guest. This is done with memory_region_add_subregion_overlap(), which allows the region to overlap any other region in the same container, and specifies a priority that allows the core to decide which of two regions at the same address are visible (highest wins). +Priority values are signed, and the default value is zero. This means that +you can use memory_region_add_subregion_overlap() both to specify a region +that must sit 'above' any others (with a positive priority) and also a +background region that sits 'below' others (with a negative priority). + +If the higher priority region in an overlap is a container or alias, then +the lower priority region will appear in any "holes" that the higher priority +region has left by not mapping subregions to that area of its address range. +(This applies recursively -- if the subregions are themselves containers or +aliases that leave holes then the lower priority region will appear in these +holes too.) + +For example, suppose we have a container A of size 0x8000 with two subregions +B and C. B is a container mapped at 0x2000, size 0x4000, priority 1; C is +an MMIO region mapped at 0x0, size 0x6000, priority 2. B currently has two +of its own subregions: D of size 0x1000 at offset 0 and E of size 0x1000 at +offset 0x2000. As a diagram: + + 0 1000 2000 3000 4000 5000 6000 7000 8000 + |------|------|------|------|------|------|------|-------| + A: [ ] + C: [CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC] + B: [ ] + D: [DDDDD] + E: [EEEEE] + +The regions that will be seen within this address range then are: + [CCCCCCCCCCCC][DDDDD][CCCCC][EEEEE][CCCCC] + +Since B has higher priority than C, its subregions appear in the flat map +even where they overlap with C. In ranges where B has not mapped anything +C's region appears. + +If B had provided its own MMIO operations (ie it was not a pure container) +then these would be used for any addresses in its range not handled by +D or E, and the result would be: + [CCCCCCCCCCCC][DDDDD][BBBBB][EEEEE][BBBBB] + +Priority values are local to a container, because the priorities of two +regions are only compared when they are both children of the same container. +This means that the device in charge of the container (typically modelling +a bus or a memory controller) can use them to manage the interaction of +its child regions without any side effects on other parts of the system. +In the example above, the priorities of D and E are unimportant because +they do not overlap each other. It is the relative priority of B and C +that causes D and E to appear on top of C: D and E's priorities are never +compared against the priority of C. Visibility ---------- @@ -90,11 +146,19 @@ guest accesses an address: descending priority order - if the address lies outside the region offset/size, the subregion is discarded - - if the subregion is a leaf (RAM or MMIO), the search terminates + - if the subregion is a leaf (RAM or MMIO), the search terminates, returning + this leaf region - if the subregion is a container, the same algorithm is used within the subregion (after the address is adjusted by the subregion offset) - - if the subregion is an alias, the search is continues at the alias target + - if the subregion is an alias, the search is continued at the alias target (after the address is adjusted by the subregion offset and alias offset) + - if a recursive search within a container or alias subregion does not + find a match (because of a "hole" in the container's coverage of its + address range), then if this is a container with its own MMIO or RAM + backing the search terminates, returning the container itself. Otherwise + we continue with the next subregion in priority order +- if none of the subregions match the address then the search terminates + with no match found Example memory map ------------------ diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 91f44d01b9..0728f36c65 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -164,7 +164,7 @@ This example allows using both of the following example objects: { "file": "my_existing_block_device_id" } { "file": { "driver": "file", "readonly": false, - 'filename': "/tmp/mydisk.qcow2" } } + "filename": "/tmp/mydisk.qcow2" } } === Commands === diff --git a/dump.c b/dump.c index 846155cbc9..80a9116c77 100644 --- a/dump.c +++ b/dump.c @@ -66,7 +66,7 @@ typedef struct DumpState { uint32_t sh_info; bool have_section; bool resume; - size_t note_size; + ssize_t note_size; hwaddr memory_offset; int fd; @@ -765,7 +765,7 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, s->note_size = cpu_get_note_size(s->dump_info.d_class, s->dump_info.d_machine, nr_cpus); - if (ret < 0) { + if (s->note_size < 0) { error_set(errp, QERR_UNSUPPORTED); goto cleanup; } diff --git a/exec.c b/exec.c index 2e31ffcb2c..95c4356c65 100644 --- a/exec.c +++ b/exec.c @@ -409,8 +409,10 @@ static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) #else static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { - tb_invalidate_phys_addr(cpu_get_phys_page_debug(cpu, pc) | - (pc & ~TARGET_PAGE_MASK)); + hwaddr phys = cpu_get_phys_page_debug(cpu, pc); + if (phys != -1) { + tb_invalidate_phys_addr(phys | (pc & ~TARGET_PAGE_MASK)); + } } #endif #endif /* TARGET_HAS_ICE */ @@ -2099,7 +2101,9 @@ void *address_space_map(AddressSpace *as, if (bounce.buffer) { return NULL; } - bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, TARGET_PAGE_SIZE); + /* Avoid unbounded allocations */ + l = MIN(l, TARGET_PAGE_SIZE); + bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l); bounce.addr = addr; bounce.len = l; diff --git a/gdbstub.c b/gdbstub.c index 0e5a3f5bf9..e8ab0b2992 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -368,9 +368,6 @@ static inline void gdb_continue(GDBState *s) #ifdef CONFIG_USER_ONLY s->running_state = 1; #else - if (runstate_check(RUN_STATE_GUEST_PANICKED)) { - runstate_set(RUN_STATE_DEBUG); - } if (!runstate_needs_reset()) { vm_start(); } diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 0243d6aa0e..d91b9cc6c6 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -18,6 +18,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += net/ devices-dirs-$(CONFIG_SOFTMMU) += nvram/ devices-dirs-$(CONFIG_SOFTMMU) += pci/ devices-dirs-$(CONFIG_PCI) += pci-bridge/ pci-host/ +devices-dirs-$(CONFIG_SOFTMMU) += pcmcia/ devices-dirs-$(CONFIG_SOFTMMU) += scsi/ devices-dirs-$(CONFIG_SOFTMMU) += sd/ devices-dirs-$(CONFIG_SOFTMMU) += ssi/ diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 7138139d32..58308a3406 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -170,8 +170,7 @@ static void acpi_table_install(const char unsigned *blob, size_t bloblen, } /* increase number of tables */ - cpu_to_le16wu((uint16_t *)acpi_tables, - le16_to_cpupu((uint16_t *)acpi_tables) + 1u); + stw_le_p(acpi_tables, lduw_le_p(acpi_tables) + 1u); /* Update the header fields. The strings need not be NUL-terminated. */ changed_fields = 0; @@ -309,6 +308,46 @@ out: error_propagate(errp, err); } +static bool acpi_table_builtin = false; + +void acpi_table_add_builtin(const QemuOpts *opts, Error **errp) +{ + acpi_table_builtin = true; + acpi_table_add(opts, errp); +} + +unsigned acpi_table_len(void *current) +{ + struct acpi_table_header *hdr = current - sizeof(hdr->_length); + return hdr->_length; +} + +static +void *acpi_table_hdr(void *h) +{ + struct acpi_table_header *hdr = h; + return &hdr->sig; +} + +uint8_t *acpi_table_first(void) +{ + if (acpi_table_builtin || !acpi_tables) { + return NULL; + } + return acpi_table_hdr(acpi_tables + ACPI_TABLE_PFX_SIZE); +} + +uint8_t *acpi_table_next(uint8_t *current) +{ + uint8_t *next = current + acpi_table_len(current); + + if (next - acpi_tables >= acpi_tables_len) { + return NULL; + } else { + return acpi_table_hdr(next); + } +} + static void acpi_notify_wakeup(Notifier *notifier, void *data) { ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup); diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 3fb443d06d..7e0429e0f9 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -24,6 +24,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "hw/hw.h" +#include "qapi/visitor.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" #include "qemu/timer.h" @@ -228,3 +229,26 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, pm->powerdown_notifier.notify = pm_powerdown_req; qemu_register_powerdown_notifier(&pm->powerdown_notifier); } + +static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + ICH9LPCPMRegs *pm = opaque; + uint32_t value = pm->pm_io_base + ICH9_PMIO_GPE0_STS; + + visit_type_uint32(v, &value, name, errp); +} + +void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) +{ + static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN; + + object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, + &pm->pm_io_base, errp); + object_property_add(obj, ACPI_PM_PROP_GPE0_BLK, "uint32", + ich9_pm_get_gpe0_blk, + NULL, NULL, pm, NULL); + object_property_add_uint32_ptr(obj, ACPI_PM_PROP_GPE0_BLK_LEN, + &gpe0_len, errp); +} diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index b46bd5ea6a..93849c8d36 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -29,6 +29,7 @@ #include "exec/ioport.h" #include "hw/nvram/fw_cfg.h" #include "exec/address-spaces.h" +#include "hw/acpi/piix4.h" //#define DEBUG @@ -69,6 +70,8 @@ typedef struct PIIX4PMState { /*< public >*/ MemoryRegion io; + uint32_t io_base; + MemoryRegion io_gpe; MemoryRegion io_pci; MemoryRegion io_cpu; @@ -152,14 +155,13 @@ static void apm_ctrl_changed(uint32_t val, void *arg) static void pm_io_space_update(PIIX4PMState *s) { PCIDevice *d = PCI_DEVICE(s); - uint32_t pm_io_base; - pm_io_base = le32_to_cpu(*(uint32_t *)(d->config + 0x40)); - pm_io_base &= 0xffc0; + s->io_base = le32_to_cpu(*(uint32_t *)(d->config + 0x40)); + s->io_base &= 0xffc0; memory_region_transaction_begin(); memory_region_set_enabled(&s->io, d->config[0x80] & 1); - memory_region_set_address(&s->io, pm_io_base); + memory_region_set_address(&s->io, s->io_base); memory_region_transaction_commit(); } @@ -326,7 +328,7 @@ static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots) if (pc->no_hotplug) { slot_free = false; } else { - qdev_free(qdev); + object_unparent(OBJECT(qdev)); } } } @@ -407,6 +409,28 @@ static void piix4_pm_machine_ready(Notifier *n, void *opaque) (memory_region_present(io_as, 0x2f8) ? 0x90 : 0); } +static void piix4_pm_add_propeties(PIIX4PMState *s) +{ + static const uint8_t acpi_enable_cmd = ACPI_ENABLE; + static const uint8_t acpi_disable_cmd = ACPI_DISABLE; + static const uint32_t gpe0_blk = GPE_BASE; + static const uint32_t gpe0_blk_len = GPE_LEN; + static const uint16_t sci_int = 9; + + object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_ENABLE_CMD, + &acpi_enable_cmd, NULL); + object_property_add_uint8_ptr(OBJECT(s), ACPI_PM_PROP_ACPI_DISABLE_CMD, + &acpi_disable_cmd, NULL); + object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_GPE0_BLK, + &gpe0_blk, NULL); + object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_GPE0_BLK_LEN, + &gpe0_blk_len, NULL); + object_property_add_uint16_ptr(OBJECT(s), ACPI_PM_PROP_SCI_INT, + &sci_int, NULL); + object_property_add_uint32_ptr(OBJECT(s), ACPI_PM_PROP_PM_IO_BASE, + &s->io_base, NULL); +} + static int piix4_pm_initfn(PCIDevice *dev) { PIIX4PMState *s = PIIX4_PM(dev); @@ -456,9 +480,21 @@ static int piix4_pm_initfn(PCIDevice *dev) piix4_acpi_system_hot_add_init(pci_address_space_io(dev), dev->bus, s); + piix4_pm_add_propeties(s); return 0; } +Object *piix4_pm_find(void) +{ + bool ambig; + Object *o = object_resolve_path_type("", TYPE_PIIX4_PM, &ambig); + + if (ambig || !o) { + return NULL; + } + return o; +} + i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, qemu_irq sci_irq, qemu_irq smi_irq, int kvm_enabled, FWCfgState *fw_cfg) @@ -489,9 +525,9 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, static Property piix4_pm_properties[] = { DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0), - DEFINE_PROP_UINT8("disable_s3", PIIX4PMState, disable_s3, 0), - DEFINE_PROP_UINT8("disable_s4", PIIX4PMState, disable_s4, 0), - DEFINE_PROP_UINT8("s4_val", PIIX4PMState, s4_val, 2), + DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0), + DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0), + DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 89a9015de7..397e8dfb37 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -11,6 +11,8 @@ #include "hw/arm/arm.h" #include "hw/loader.h" #include "elf.h" +#include "sysemu/qtest.h" +#include "qemu/error-report.h" /* Bitbanded IO. Each word corresponds to a single bit. */ @@ -232,21 +234,22 @@ qemu_irq *armv7m_init(MemoryRegion *address_space_mem, big_endian = 0; #endif - if (!kernel_filename) { + if (!kernel_filename && !qtest_enabled()) { fprintf(stderr, "Guest image must be specified (using -kernel)\n"); exit(1); } - image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, - NULL, big_endian, ELF_MACHINE, 1); - if (image_size < 0) { - image_size = load_image_targphys(kernel_filename, 0, flash_size); - lowaddr = 0; - } - if (image_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - kernel_filename); - exit(1); + if (kernel_filename) { + image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, + NULL, big_endian, ELF_MACHINE, 1); + if (image_size < 0) { + image_size = load_image_targphys(kernel_filename, 0, flash_size); + lowaddr = 0; + } + if (image_size < 0) { + error_report("Could not load kernel '%s'", kernel_filename); + exit(1); + } } /* Hack to map an additional page of ram at the top of the address diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 1e313afe8d..583ec7992e 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -354,8 +354,10 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) /* Load the kernel. */ if (!info->kernel_filename) { - fprintf(stderr, "Kernel image must be specified\n"); - exit(1); + /* If no kernel specified, do nothing; we will start from address 0 + * (typically a boot ROM image) in the same way as hardware. + */ + return; } info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 2929f9f8ab..26cedecee3 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -22,6 +22,7 @@ */ #include "sysemu/sysemu.h" +#include "sysemu/qtest.h" #include "hw/sysbus.h" #include "net/net.h" #include "hw/arm/arm.h" @@ -96,7 +97,7 @@ static void lan9215_init(uint32_t base, qemu_irq irq) static Exynos4210State *exynos4_boards_init_common(QEMUMachineInitArgs *args, Exynos4BoardType board_type) { - if (smp_cpus != EXYNOS4210_NCPUS) { + if (smp_cpus != EXYNOS4210_NCPUS && !qtest_enabled()) { fprintf(stderr, "%s board supports only %d CPU cores. Ignoring smp_cpus" " value.\n", exynos4_machines[board_type].name, diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c index e97fbbd231..aeea17295b 100644 --- a/hw/arm/gumstix.c +++ b/hw/arm/gumstix.c @@ -42,6 +42,7 @@ #include "hw/boards.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" +#include "sysemu/qtest.h" static const int sector_len = 128 * 1024; @@ -58,7 +59,7 @@ static void connex_init(QEMUMachineInitArgs *args) cpu = pxa255_init(address_space_mem, connex_ram); dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo) { + if (!dinfo && !qtest_enabled()) { fprintf(stderr, "A flash image must be given with the " "'pflash' parameter\n"); exit(1); @@ -70,7 +71,8 @@ static void connex_init(QEMUMachineInitArgs *args) be = 0; #endif if (!pflash_cfi01_register(0x00000000, NULL, "connext.rom", connex_rom, - dinfo->bdrv, sector_len, connex_rom / sector_len, + dinfo ? dinfo->bdrv : NULL, + sector_len, connex_rom / sector_len, 2, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory.\n"); exit(1); @@ -95,7 +97,7 @@ static void verdex_init(QEMUMachineInitArgs *args) cpu = pxa270_init(address_space_mem, verdex_ram, cpu_model ?: "pxa270-c0"); dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo) { + if (!dinfo && !qtest_enabled()) { fprintf(stderr, "A flash image must be given with the " "'pflash' parameter\n"); exit(1); @@ -107,7 +109,8 @@ static void verdex_init(QEMUMachineInitArgs *args) be = 0; #endif if (!pflash_cfi01_register(0x00000000, NULL, "verdex.rom", verdex_rom, - dinfo->bdrv, sector_len, verdex_rom / sector_len, + dinfo ? dinfo->bdrv : NULL, + sector_len, verdex_rom / sector_len, 2, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory.\n"); exit(1); diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 2ef93ed8d6..c44b2a499c 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -11,6 +11,7 @@ #include "hw/devices.h" #include "hw/boards.h" #include "hw/arm/arm.h" +#include "hw/misc/arm_integrator_debug.h" #include "net/net.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" @@ -508,6 +509,7 @@ static void integratorcp_init(QEMUMachineInitArgs *args) icp_control_init(0xcb000000); sysbus_create_simple("pl050_keyboard", 0x18000000, pic[3]); sysbus_create_simple("pl050_mouse", 0x19000000, pic[4]); + sysbus_create_simple(TYPE_INTEGRATOR_DEBUG, 0x1a000000, 0); sysbus_create_varargs("pl181", 0x1c000000, pic[23], pic[24], NULL); if (nd_table[0].used) smc91c111_init(&nd_table[0], 0xc8000000, pic[27]); diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c index b244f7e112..9402c841e9 100644 --- a/hw/arm/mainstone.c +++ b/hw/arm/mainstone.c @@ -21,6 +21,7 @@ #include "sysemu/blockdev.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" +#include "sysemu/qtest.h" /* Device addresses */ #define MST_FPGA_PHYS 0x08000000 @@ -127,6 +128,9 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, for (i = 0; i < 2; i ++) { dinfo = drive_get(IF_PFLASH, 0, i); if (!dinfo) { + if (qtest_enabled()) { + break; + } fprintf(stderr, "Two flash images must be given with the " "'pflash' parameter\n"); exit(1); @@ -147,7 +151,6 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, qdev_get_gpio_in(mpu->gpio, 0)); /* setup keypad */ - printf("map addr %p\n", &map); pxa27x_register_keypad(mpu->kp, map, 0xe0); /* MMC/SD host */ diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index b0f8664607..3ba263ab4d 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -32,6 +32,7 @@ #include "hw/arm/arm.h" #include "hw/block/flash.h" #include "sysemu/blockdev.h" +#include "sysemu/qtest.h" #include "exec/address-spaces.h" /*****************************************************************************/ @@ -188,18 +189,16 @@ static void sx1_init(QEMUMachineInitArgs *args, const int version) OMAP_CS1_BASE, &cs[1]); } - if (!args->kernel_filename && !fl_idx) { + if (!args->kernel_filename && !fl_idx && !qtest_enabled()) { fprintf(stderr, "Kernel or Flash image must be specified\n"); exit(1); } /* Load the kernel. */ - if (args->kernel_filename) { - sx1_binfo.kernel_filename = args->kernel_filename; - sx1_binfo.kernel_cmdline = args->kernel_cmdline; - sx1_binfo.initrd_filename = args->initrd_filename; - arm_load_kernel(mpu->cpu, &sx1_binfo); - } + sx1_binfo.kernel_filename = args->kernel_filename; + sx1_binfo.kernel_cmdline = args->kernel_cmdline; + sx1_binfo.initrd_filename = args->initrd_filename; + arm_load_kernel(mpu->cpu, &sx1_binfo); /* TODO: fix next line */ //~ qemu_console_resize(ds, 640, 480); diff --git a/hw/arm/palm.c b/hw/arm/palm.c index 3e390448e2..fac4f69807 100644 --- a/hw/arm/palm.c +++ b/hw/arm/palm.c @@ -19,6 +19,7 @@ #include "hw/hw.h" #include "audio/audio.h" #include "sysemu/sysemu.h" +#include "sysemu/qtest.h" #include "ui/console.h" #include "hw/arm/omap.h" #include "hw/boards.h" @@ -255,18 +256,16 @@ static void palmte_init(QEMUMachineInitArgs *args) } } - if (!rom_loaded && !kernel_filename) { + if (!rom_loaded && !kernel_filename && !qtest_enabled()) { fprintf(stderr, "Kernel or ROM image must be specified\n"); exit(1); } /* Load the kernel. */ - if (kernel_filename) { - palmte_binfo.kernel_filename = kernel_filename; - palmte_binfo.kernel_cmdline = kernel_cmdline; - palmte_binfo.initrd_filename = initrd_filename; - arm_load_kernel(mpu->cpu, &palmte_binfo); - } + palmte_binfo.kernel_filename = kernel_filename; + palmte_binfo.kernel_cmdline = kernel_cmdline; + palmte_binfo.initrd_filename = initrd_filename; + arm_load_kernel(mpu->cpu, &palmte_binfo); } static QEMUMachine palmte_machine = { diff --git a/hw/arm/z2.c b/hw/arm/z2.c index 2e0d5d4bcc..d52c5019b3 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -24,6 +24,7 @@ #include "ui/console.h" #include "audio/audio.h" #include "exec/address-spaces.h" +#include "sysemu/qtest.h" #ifdef DEBUG_Z2 #define DPRINTF(fmt, ...) \ @@ -323,7 +324,7 @@ static void z2_init(QEMUMachineInitArgs *args) be = 0; #endif dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo) { + if (!dinfo && !qtest_enabled()) { fprintf(stderr, "Flash image must be given with the " "'pflash' parameter\n"); exit(1); @@ -331,7 +332,7 @@ static void z2_init(QEMUMachineInitArgs *args) if (!pflash_cfi01_register(Z2_FLASH_BASE, NULL, "z2.flash0", Z2_FLASH_SIZE, - dinfo->bdrv, sector_len, + dinfo ? dinfo->bdrv : NULL, sector_len, Z2_FLASH_SIZE / sector_len, 4, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory.\n"); @@ -360,13 +361,11 @@ static void z2_init(QEMUMachineInitArgs *args) qdev_connect_gpio_out(mpu->gpio, Z2_GPIO_LCD_CS, qemu_allocate_irqs(z2_lcd_cs, z2_lcd, 1)[0]); - if (kernel_filename) { - z2_binfo.kernel_filename = kernel_filename; - z2_binfo.kernel_cmdline = kernel_cmdline; - z2_binfo.initrd_filename = initrd_filename; - z2_binfo.board_id = 0x6dd; - arm_load_kernel(mpu->cpu, &z2_binfo); - } + z2_binfo.kernel_filename = kernel_filename; + z2_binfo.kernel_cmdline = kernel_cmdline; + z2_binfo.initrd_filename = initrd_filename; + z2_binfo.board_id = 0x6dd; + arm_load_kernel(mpu->cpu, &z2_binfo); } static QEMUMachine z2_machine = { diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 01b4dfbc67..03f484628e 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -280,12 +280,12 @@ static void update_sr (AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr) if (level) { s->glob_sta |= masks[r - s->bm_regs]; dolog ("set irq level=1\n"); - qemu_set_irq (s->dev.irq[0], 1); + pci_irq_assert(&s->dev); } else { s->glob_sta &= ~masks[r - s->bm_regs]; dolog ("set irq level=0\n"); - qemu_set_irq (s->dev.irq[0], 0); + pci_irq_deassert(&s->dev); } } diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index adb66ced71..1ec7acee02 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -323,7 +323,7 @@ static void es1370_update_status (ES1370State *s, uint32_t new_status) else { s->status = new_status & ~STAT_INTR; } - qemu_set_irq (s->dev.irq[0], !!level); + pci_set_irq(&s->dev, !!level); } static void es1370_reset (ES1370State *s) @@ -349,7 +349,7 @@ static void es1370_reset (ES1370State *s) s->dac_voice[i] = NULL; } } - qemu_irq_lower (s->dev.irq[0]); + pci_irq_deassert(&s->dev); } static void es1370_maybe_lower_irq (ES1370State *s, uint32_t sctl) diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index a6666c6cdf..4327264394 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -269,7 +269,7 @@ static void intel_hda_update_irq(IntelHDAState *d) msi_notify(&d->pci, 0); } } else { - qemu_set_irq(d->pci.irq[0], level); + pci_set_irq(&d->pci, level); } } diff --git a/hw/block/cdrom.c b/hw/block/cdrom.c index 38469fa928..4e1019c890 100644 --- a/hw/block/cdrom.c +++ b/hw/block/cdrom.c @@ -59,7 +59,7 @@ int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track) q += 3; } else { /* sector 0 */ - cpu_to_be32wu((uint32_t *)q, 0); + stl_be_p(q, 0); q += 4; } } @@ -73,11 +73,11 @@ int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track) lba_to_msf(q, nb_sectors); q += 3; } else { - cpu_to_be32wu((uint32_t *)q, nb_sectors); + stl_be_p(q, nb_sectors); q += 4; } len = q - buf; - cpu_to_be16wu((uint16_t *)buf, len - 2); + stw_be_p(buf, len - 2); return len; } @@ -127,7 +127,7 @@ int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num) lba_to_msf(q, nb_sectors); q += 3; } else { - cpu_to_be32wu((uint32_t *)q, nb_sectors); + stl_be_p(q, nb_sectors); q += 4; } @@ -150,6 +150,6 @@ int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num) } len = q - buf; - cpu_to_be16wu((uint16_t *)buf, len - 2); + stw_be_p(buf, len - 2); return len; } diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 5dee229734..2882ffefce 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -69,7 +69,7 @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq) if (msix_enabled(&(n->parent_obj))) { msix_notify(&(n->parent_obj), cq->vector); } else { - qemu_irq_pulse(n->parent_obj.irq[0]); + pci_irq_pulse(&n->parent_obj); } } } diff --git a/hw/block/tc58128.c b/hw/block/tc58128.c index a3929d444f..728f1c3b68 100644 --- a/hw/block/tc58128.c +++ b/hw/block/tc58128.c @@ -1,6 +1,8 @@ #include "hw/hw.h" #include "hw/sh4/sh.h" #include "hw/loader.h" +#include "sysemu/qtest.h" +#include "qemu/error-report.h" #define CE1 0x0100 #define CE2 0x0200 @@ -36,10 +38,10 @@ static void init_dev(tc58128_dev * dev, const char *filename) /* Load flash image skipping the first block */ ret = load_image(filename, dev->flash_contents + 528 * 32); if (ret < 0) { - fprintf(stderr, "ret=%d\n", ret); - fprintf(stderr, "qemu: could not load flash image %s\n", - filename); - exit(1); + if (!qtest_enabled()) { + error_report("Could not load flash image %s", filename); + exit(1); + } } else { /* Build first block with number of blocks */ blocks = (ret + 528 * 32 - 1) / (528 * 32); diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c index 2e4b5c58b0..2c52a0fa8e 100644 --- a/hw/char/milkymist-uart.c +++ b/hw/char/milkymist-uart.c @@ -195,22 +195,26 @@ static void milkymist_uart_reset(DeviceState *d) s->regs[R_STAT] = STAT_THRE; } -static int milkymist_uart_init(SysBusDevice *dev) +static void milkymist_uart_realize(DeviceState *dev, Error **errp) { MilkymistUartState *s = MILKYMIST_UART(dev); - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s, - "milkymist-uart", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - s->chr = qemu_char_get_next_serial(); if (s->chr) { qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); } +} - return 0; +static void milkymist_uart_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MilkymistUartState *s = MILKYMIST_UART(obj); + + sysbus_init_irq(sbd, &s->irq); + + memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s, + "milkymist-uart", R_MAX * 4); + sysbus_init_mmio(sbd, &s->regs_region); } static const VMStateDescription vmstate_milkymist_uart = { @@ -227,9 +231,8 @@ static const VMStateDescription vmstate_milkymist_uart = { static void milkymist_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = milkymist_uart_init; + dc->realize = milkymist_uart_realize; dc->reset = milkymist_uart_reset; dc->vmsd = &vmstate_milkymist_uart; } @@ -238,6 +241,7 @@ static const TypeInfo milkymist_uart_info = { .name = TYPE_MILKYMIST_UART, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistUartState), + .instance_init = milkymist_uart_init, .class_init = milkymist_uart_class_init, }; diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index aec6705a01..991c99fa6e 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -61,7 +61,7 @@ static int serial_pci_init(PCIDevice *dev) } pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; - s->irq = pci->dev.irq[0]; + s->irq = pci_allocate_irq(&pci->dev); memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, "serial", 8); pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io); @@ -79,7 +79,7 @@ static void multi_serial_irq_mux(void *opaque, int n, int level) pending = 1; } } - qemu_set_irq(pci->dev.irq[0], pending); + pci_set_irq(&pci->dev, pending); } static int multi_serial_pci_init(PCIDevice *dev) @@ -132,6 +132,7 @@ static void serial_pci_exit(PCIDevice *dev) serial_exit_core(s); memory_region_destroy(&s->io); + qemu_free_irq(s->irq); } static void multi_serial_pci_exit(PCIDevice *dev) diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 9c2aef82e6..f8a4981e27 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -168,6 +168,7 @@ static void spapr_vty_class_init(ObjectClass *klass, void *data) k->dt_name = "vty"; k->dt_type = "serial"; k->dt_compatible = "hvterm1"; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); dc->props = spapr_vty_properties; dc->vmsd = &vmstate_spapr_vty; } diff --git a/hw/char/tpci200.c b/hw/char/tpci200.c index e04ff26019..a49d2ed5c1 100644 --- a/hw/char/tpci200.c +++ b/hw/char/tpci200.c @@ -134,8 +134,8 @@ static void tpci200_set_irq(void *opaque, int intno, int level) /* Check if the interrupt is edge sensitive */ if (dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) { if (level) { - qemu_set_irq(dev->dev.irq[0], !dev->int_set); - qemu_set_irq(dev->dev.irq[0], dev->int_set); + pci_set_irq(&dev->dev, !dev->int_set); + pci_set_irq(&dev->dev, dev->int_set); } } else { unsigned i, j; @@ -153,10 +153,10 @@ static void tpci200_set_irq(void *opaque, int intno, int level) } if (level_status && !dev->int_set) { - qemu_irq_raise(dev->dev.irq[0]); + pci_irq_assert(&dev->dev); dev->int_set = 1; } else if (!level_status && dev->int_set) { - qemu_irq_lower(dev->dev.irq[0]); + pci_irq_deassert(&dev->dev); dev->int_set = 0; } } diff --git a/hw/core/irq.c b/hw/core/irq.c index 20785428ef..03c8cb31ea 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -68,6 +68,17 @@ qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n) return qemu_extend_irqs(NULL, 0, handler, opaque, n); } +qemu_irq qemu_allocate_irq(qemu_irq_handler handler, void *opaque, int n) +{ + struct IRQState *irq; + + irq = g_new(struct IRQState, 1); + irq->handler = handler; + irq->opaque = opaque; + irq->n = n; + + return irq; +} void qemu_free_irqs(qemu_irq *s) { @@ -75,6 +86,11 @@ void qemu_free_irqs(qemu_irq *s) g_free(s); } +void qemu_free_irq(qemu_irq irq) +{ + g_free(irq); +} + static void qemu_notirq(void *opaque, int line, int level) { struct IRQState *irq = opaque; diff --git a/hw/core/loader.c b/hw/core/loader.c index 7b3d3ee6a0..60d2ebd4ac 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -663,7 +663,7 @@ int rom_add_file(const char *file, const char *fw_dir, rom_insert(rom); if (rom->fw_file && fw_cfg) { const char *basename; - char fw_file_name[56]; + char fw_file_name[FW_CFG_MAX_FILE_PATH]; void *data; basename = strrchr(rom->fw_file, '/'); @@ -700,10 +700,12 @@ err: return -1; } -int rom_add_blob(const char *name, const void *blob, size_t len, - hwaddr addr) +void *rom_add_blob(const char *name, const void *blob, size_t len, + hwaddr addr, const char *fw_file_name, + FWCfgReadCallback fw_callback, void *callback_opaque) { Rom *rom; + void *data = NULL; rom = g_malloc0(sizeof(*rom)); rom->name = g_strdup(name); @@ -713,7 +715,22 @@ int rom_add_blob(const char *name, const void *blob, size_t len, rom->data = g_malloc0(rom->datasize); memcpy(rom->data, blob, len); rom_insert(rom); - return 0; + if (fw_file_name && fw_cfg) { + char devpath[100]; + + snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); + + if (rom_file_in_ram) { + data = rom_set_mr(rom, OBJECT(fw_cfg), devpath); + } else { + data = rom->data; + } + + fw_cfg_add_file_callback(fw_cfg, fw_file_name, + fw_callback, callback_opaque, + data, rom->romsize); + } + return data; } /* This function is specific for elf program because we don't need to allocate @@ -795,10 +812,14 @@ int rom_load_all(void) memory_region_unref(section.mr); } qemu_register_reset(rom_reset, NULL); - roms_loaded = 1; return 0; } +void rom_load_done(void) +{ + roms_loaded = 1; +} + void rom_set_fw(FWCfgState *f) { fw_cfg = f; diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 533f6dd122..e374a9399f 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -164,7 +164,7 @@ int qdev_init(DeviceState *dev) if (local_err != NULL) { qerror_report_err(local_err); error_free(local_err); - qdev_free(dev); + object_unparent(OBJECT(dev)); return -1; } return 0; @@ -258,7 +258,7 @@ void qbus_reset_all_fn(void *opaque) int qdev_simple_unplug_cb(DeviceState *dev) { /* just zap it */ - qdev_free(dev); + object_unparent(OBJECT(dev)); return 0; } @@ -280,12 +280,6 @@ void qdev_init_nofail(DeviceState *dev) } } -/* Unlink device from bus and free the structure. */ -void qdev_free(DeviceState *dev) -{ - object_unparent(OBJECT(dev)); -} - void qdev_machine_creation_done(void) { /* @@ -458,7 +452,7 @@ static void bus_unparent(Object *obj) while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { DeviceState *dev = kid->child; - qdev_free(dev); + object_unparent(OBJECT(dev)); } if (bus->parent) { QLIST_REMOVE(bus, sibling); diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index b84cd4a16f..146f50aa15 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -49,7 +49,7 @@ void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq) } static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr, - bool may_overlap, unsigned priority) + bool may_overlap, int priority) { assert(n >= 0 && n < dev->num_mmio); @@ -81,7 +81,7 @@ void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr) } void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, - unsigned priority) + int priority) { sysbus_mmio_map_common(dev, n, addr, true, priority); } diff --git a/hw/cpu/Makefile.objs b/hw/cpu/Makefile.objs index df287c1d8c..6381238cc5 100644 --- a/hw/cpu/Makefile.objs +++ b/hw/cpu/Makefile.objs @@ -1,4 +1,5 @@ obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o +obj-$(CONFIG_REALVIEW) += realview_mpcore.o obj-$(CONFIG_A9MPCORE) += a9mpcore.o obj-$(CONFIG_A15MPCORE) += a15mpcore.o obj-$(CONFIG_ICC_BUS) += icc_bus.o diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 9abba67632..acc419e11a 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -18,55 +18,60 @@ * with this program; if not, see <http://www.gnu.org/licenses/>. */ -#include "hw/sysbus.h" +#include "hw/cpu/a15mpcore.h" #include "sysemu/kvm.h" -/* A15MP private memory region. */ - -#define TYPE_A15MPCORE_PRIV "a15mpcore_priv" -#define A15MPCORE_PRIV(obj) \ - OBJECT_CHECK(A15MPPrivState, (obj), TYPE_A15MPCORE_PRIV) - -typedef struct A15MPPrivState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - uint32_t num_cpu; - uint32_t num_irq; - MemoryRegion container; - DeviceState *gic; -} A15MPPrivState; - static void a15mp_priv_set_irq(void *opaque, int irq, int level) { A15MPPrivState *s = (A15MPPrivState *)opaque; - qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); + + qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level); } -static int a15mp_priv_init(SysBusDevice *dev) +static void a15mp_priv_initfn(Object *obj) { - A15MPPrivState *s = A15MPCORE_PRIV(dev); - SysBusDevice *busdev; + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + A15MPPrivState *s = A15MPCORE_PRIV(obj); + DeviceState *gicdev; const char *gictype = "arm_gic"; - int i; if (kvm_irqchip_in_kernel()) { gictype = "kvm-arm-gic"; } - s->gic = qdev_create(NULL, gictype); - qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); - qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); - qdev_prop_set_uint32(s->gic, "revision", 2); - qdev_init_nofail(s->gic); - busdev = SYS_BUS_DEVICE(s->gic); + memory_region_init(&s->container, obj, "a15mp-priv-container", 0x8000); + sysbus_init_mmio(sbd, &s->container); + + object_initialize(&s->gic, sizeof(s->gic), gictype); + gicdev = DEVICE(&s->gic); + qdev_set_parent_bus(gicdev, sysbus_get_default()); + qdev_prop_set_uint32(gicdev, "revision", 2); +} + +static void a15mp_priv_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + A15MPPrivState *s = A15MPCORE_PRIV(dev); + DeviceState *gicdev; + SysBusDevice *busdev; + int i; + Error *err = NULL; + + gicdev = DEVICE(&s->gic); + qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); + qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + busdev = SYS_BUS_DEVICE(&s->gic); /* Pass through outbound IRQ lines from the GIC */ - sysbus_pass_irq(dev, busdev); + sysbus_pass_irq(sbd, busdev); /* Pass through inbound GPIO lines to the GIC */ - qdev_init_gpio_in(DEVICE(dev), a15mp_priv_set_irq, s->num_irq - 32); + qdev_init_gpio_in(dev, a15mp_priv_set_irq, s->num_irq - 32); /* Wire the outputs from each CPU's generic timer to the * appropriate GIC PPI inputs @@ -78,10 +83,10 @@ static int a15mp_priv_init(SysBusDevice *dev) * since a real A15 always has TrustZone but QEMU doesn't. */ qdev_connect_gpio_out(cpudev, 0, - qdev_get_gpio_in(s->gic, ppibase + 30)); + qdev_get_gpio_in(gicdev, ppibase + 30)); /* virtual timer */ qdev_connect_gpio_out(cpudev, 1, - qdev_get_gpio_in(s->gic, ppibase + 27)); + qdev_get_gpio_in(gicdev, ppibase + 27)); } /* Memory map (addresses are offsets from PERIPHBASE): @@ -92,15 +97,10 @@ static int a15mp_priv_init(SysBusDevice *dev) * 0x5000-0x5fff -- GIC virtual interface control (not modelled) * 0x6000-0x7fff -- GIC virtual CPU interface (not modelled) */ - memory_region_init(&s->container, OBJECT(s), - "a15mp-priv-container", 0x8000); memory_region_add_subregion(&s->container, 0x1000, sysbus_mmio_get_region(busdev, 0)); memory_region_add_subregion(&s->container, 0x2000, sysbus_mmio_get_region(busdev, 1)); - - sysbus_init_mmio(dev, &s->container); - return 0; } static Property a15mp_priv_properties[] = { @@ -118,8 +118,8 @@ static Property a15mp_priv_properties[] = { static void a15mp_priv_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = a15mp_priv_init; + + dc->realize = a15mp_priv_realize; dc->props = a15mp_priv_properties; /* We currently have no savable state */ } @@ -128,6 +128,7 @@ static const TypeInfo a15mp_priv_info = { .name = TYPE_A15MPCORE_PRIV, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(A15MPPrivState), + .instance_init = a15mp_priv_initfn, .class_init = a15mp_priv_class_init, }; diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index 3e675e3941..918a7d1291 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -8,64 +8,86 @@ * This code is licensed under the GPL. */ -#include "hw/sysbus.h" - -#define TYPE_A9MPCORE_PRIV "a9mpcore_priv" -#define A9MPCORE_PRIV(obj) \ - OBJECT_CHECK(A9MPPrivState, (obj), TYPE_A9MPCORE_PRIV) - -typedef struct A9MPPrivState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - uint32_t num_cpu; - MemoryRegion container; - DeviceState *mptimer; - DeviceState *wdt; - DeviceState *gic; - DeviceState *scu; - uint32_t num_irq; -} A9MPPrivState; +#include "hw/cpu/a9mpcore.h" static void a9mp_priv_set_irq(void *opaque, int irq, int level) { A9MPPrivState *s = (A9MPPrivState *)opaque; - qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); + + qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level); +} + +static void a9mp_priv_initfn(Object *obj) +{ + A9MPPrivState *s = A9MPCORE_PRIV(obj); + + memory_region_init(&s->container, obj, "a9mp-priv-container", 0x2000); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container); + + object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); + qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); + + object_initialize(&s->scu, sizeof(s->scu), TYPE_A9_SCU); + qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); + + object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER); + qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default()); + + object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ARM_MPTIMER); + qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default()); } -static int a9mp_priv_init(SysBusDevice *dev) +static void a9mp_priv_realize(DeviceState *dev, Error **errp) { + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); A9MPPrivState *s = A9MPCORE_PRIV(dev); + DeviceState *gicdev, *scudev, *mptimerdev, *wdtdev; SysBusDevice *timerbusdev, *wdtbusdev, *gicbusdev, *scubusdev; + Error *err = NULL; int i; - s->gic = qdev_create(NULL, "arm_gic"); - qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); - qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); - qdev_init_nofail(s->gic); - gicbusdev = SYS_BUS_DEVICE(s->gic); + gicdev = DEVICE(&s->gic); + qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); + qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + gicbusdev = SYS_BUS_DEVICE(&s->gic); /* Pass through outbound IRQ lines from the GIC */ - sysbus_pass_irq(dev, gicbusdev); + sysbus_pass_irq(sbd, gicbusdev); /* Pass through inbound GPIO lines to the GIC */ - qdev_init_gpio_in(DEVICE(dev), a9mp_priv_set_irq, s->num_irq - 32); - - s->scu = qdev_create(NULL, "a9-scu"); - qdev_prop_set_uint32(s->scu, "num-cpu", s->num_cpu); - qdev_init_nofail(s->scu); - scubusdev = SYS_BUS_DEVICE(s->scu); - - s->mptimer = qdev_create(NULL, "arm_mptimer"); - qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu); - qdev_init_nofail(s->mptimer); - timerbusdev = SYS_BUS_DEVICE(s->mptimer); - - s->wdt = qdev_create(NULL, "arm_mptimer"); - qdev_prop_set_uint32(s->wdt, "num-cpu", s->num_cpu); - qdev_init_nofail(s->wdt); - wdtbusdev = SYS_BUS_DEVICE(s->wdt); + qdev_init_gpio_in(dev, a9mp_priv_set_irq, s->num_irq - 32); + + scudev = DEVICE(&s->scu); + qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + scubusdev = SYS_BUS_DEVICE(&s->scu); + + mptimerdev = DEVICE(&s->mptimer); + qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->mptimer), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + timerbusdev = SYS_BUS_DEVICE(&s->mptimer); + + wdtdev = DEVICE(&s->wdt); + qdev_prop_set_uint32(wdtdev, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->wdt), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + wdtbusdev = SYS_BUS_DEVICE(&s->wdt); /* Memory map (addresses are offsets from PERIPHBASE): * 0x0000-0x00ff -- Snoop Control Unit @@ -78,7 +100,6 @@ static int a9mp_priv_init(SysBusDevice *dev) * * We should implement the global timer but don't currently do so. */ - memory_region_init(&s->container, OBJECT(s), "a9mp-priv-container", 0x2000); memory_region_add_subregion(&s->container, 0, sysbus_mmio_get_region(scubusdev, 0)); /* GIC CPU interface */ @@ -94,19 +115,16 @@ static int a9mp_priv_init(SysBusDevice *dev) memory_region_add_subregion(&s->container, 0x1000, sysbus_mmio_get_region(gicbusdev, 0)); - sysbus_init_mmio(dev, &s->container); - /* Wire up the interrupt from each watchdog and timer. * For each core the timer is PPI 29 and the watchdog PPI 30. */ for (i = 0; i < s->num_cpu; i++) { int ppibase = (s->num_irq - 32) + i * 32; sysbus_connect_irq(timerbusdev, i, - qdev_get_gpio_in(s->gic, ppibase + 29)); + qdev_get_gpio_in(gicdev, ppibase + 29)); sysbus_connect_irq(wdtbusdev, i, - qdev_get_gpio_in(s->gic, ppibase + 30)); + qdev_get_gpio_in(gicdev, ppibase + 30)); } - return 0; } static Property a9mp_priv_properties[] = { @@ -124,9 +142,8 @@ static Property a9mp_priv_properties[] = { static void a9mp_priv_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = a9mp_priv_init; + dc->realize = a9mp_priv_realize; dc->props = a9mp_priv_properties; } @@ -134,6 +151,7 @@ static const TypeInfo a9mp_priv_info = { .name = TYPE_A9MPCORE_PRIV, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(A9MPPrivState), + .instance_init = a9mp_priv_initfn, .class_init = a9mp_priv_class_init, }; diff --git a/hw/cpu/arm11mpcore.c b/hw/cpu/arm11mpcore.c index a786c628dd..717d3e4f88 100644 --- a/hw/cpu/arm11mpcore.c +++ b/hw/cpu/arm11mpcore.c @@ -7,96 +7,28 @@ * This code is licensed under the GPL. */ -#include "hw/sysbus.h" -#include "qemu/timer.h" +#include "hw/cpu/arm11mpcore.h" +#include "hw/intc/realview_gic.h" -/* MPCore private memory region. */ - -#define TYPE_ARM11MPCORE_PRIV "arm11mpcore_priv" -#define ARM11MPCORE_PRIV(obj) \ - OBJECT_CHECK(ARM11MPCorePriveState, (obj), TYPE_ARM11MPCORE_PRIV) - -typedef struct ARM11MPCorePriveState { - SysBusDevice parent_obj; - - uint32_t scu_control; - int iomemtype; - uint32_t old_timer_status[8]; - uint32_t num_cpu; - MemoryRegion iomem; - MemoryRegion container; - DeviceState *mptimer; - DeviceState *wdtimer; - DeviceState *gic; - uint32_t num_irq; -} ARM11MPCorePriveState; - -/* Per-CPU private memory mapped IO. */ - -static uint64_t mpcore_scu_read(void *opaque, hwaddr offset, - unsigned size) -{ - ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque; - int id; - /* SCU */ - switch (offset) { - case 0x00: /* Control. */ - return s->scu_control; - case 0x04: /* Configuration. */ - id = ((1 << s->num_cpu) - 1) << 4; - return id | (s->num_cpu - 1); - case 0x08: /* CPU status. */ - return 0; - case 0x0c: /* Invalidate all. */ - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "mpcore_priv_read: Bad offset %x\n", (int)offset); - return 0; - } -} - -static void mpcore_scu_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque; - /* SCU */ - switch (offset) { - case 0: /* Control register. */ - s->scu_control = value & 1; - break; - case 0x0c: /* Invalidate all. */ - /* This is a no-op as cache is not emulated. */ - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "mpcore_priv_read: Bad offset %x\n", (int)offset); - } -} - -static const MemoryRegionOps mpcore_scu_ops = { - .read = mpcore_scu_read, - .write = mpcore_scu_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; static void mpcore_priv_set_irq(void *opaque, int irq, int level) { ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque; - qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); + + qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level); } static void mpcore_priv_map_setup(ARM11MPCorePriveState *s) { int i; - SysBusDevice *gicbusdev = SYS_BUS_DEVICE(s->gic); - SysBusDevice *timerbusdev = SYS_BUS_DEVICE(s->mptimer); - SysBusDevice *wdtbusdev = SYS_BUS_DEVICE(s->wdtimer); - memory_region_init(&s->container, OBJECT(s), - "mpcode-priv-container", 0x2000); - memory_region_init_io(&s->iomem, OBJECT(s), - &mpcore_scu_ops, s, "mpcore-scu", 0x100); - memory_region_add_subregion(&s->container, 0, &s->iomem); + SysBusDevice *scubusdev = SYS_BUS_DEVICE(&s->scu); + DeviceState *gicdev = DEVICE(&s->gic); + SysBusDevice *gicbusdev = SYS_BUS_DEVICE(&s->gic); + SysBusDevice *timerbusdev = SYS_BUS_DEVICE(&s->mptimer); + SysBusDevice *wdtbusdev = SYS_BUS_DEVICE(&s->wdtimer); + + memory_region_add_subregion(&s->container, 0, + sysbus_mmio_get_region(scubusdev, 0)); /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs * at 0x200, 0x300... */ @@ -124,134 +56,84 @@ static void mpcore_priv_map_setup(ARM11MPCorePriveState *s) for (i = 0; i < s->num_cpu; i++) { int ppibase = (s->num_irq - 32) + i * 32; sysbus_connect_irq(timerbusdev, i, - qdev_get_gpio_in(s->gic, ppibase + 29)); + qdev_get_gpio_in(gicdev, ppibase + 29)); sysbus_connect_irq(wdtbusdev, i, - qdev_get_gpio_in(s->gic, ppibase + 30)); + qdev_get_gpio_in(gicdev, ppibase + 30)); } } -static int mpcore_priv_init(SysBusDevice *sbd) +static void mpcore_priv_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); ARM11MPCorePriveState *s = ARM11MPCORE_PRIV(dev); + DeviceState *scudev = DEVICE(&s->scu); + DeviceState *gicdev = DEVICE(&s->gic); + DeviceState *mptimerdev = DEVICE(&s->mptimer); + DeviceState *wdtimerdev = DEVICE(&s->wdtimer); + Error *err = NULL; + + qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } - s->gic = qdev_create(NULL, "arm_gic"); - qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); - qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); - /* Request the legacy 11MPCore GIC behaviour: */ - qdev_prop_set_uint32(s->gic, "revision", 0); - qdev_init_nofail(s->gic); + qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu); + qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq); + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } /* Pass through outbound IRQ lines from the GIC */ - sysbus_pass_irq(sbd, SYS_BUS_DEVICE(s->gic)); + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(&s->gic)); /* Pass through inbound GPIO lines to the GIC */ qdev_init_gpio_in(dev, mpcore_priv_set_irq, s->num_irq - 32); - s->mptimer = qdev_create(NULL, "arm_mptimer"); - qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu); - qdev_init_nofail(s->mptimer); + qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->mptimer), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } - s->wdtimer = qdev_create(NULL, "arm_mptimer"); - qdev_prop_set_uint32(s->wdtimer, "num-cpu", s->num_cpu); - qdev_init_nofail(s->wdtimer); + qdev_prop_set_uint32(wdtimerdev, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->wdtimer), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } mpcore_priv_map_setup(s); - sysbus_init_mmio(sbd, &s->container); - return 0; } -#define TYPE_REALVIEW_MPCORE_RIRQ "realview_mpcore" -#define REALVIEW_MPCORE_RIRQ(obj) \ - OBJECT_CHECK(mpcore_rirq_state, (obj), TYPE_REALVIEW_MPCORE_RIRQ) - -/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ - controllers. The output of these, plus some of the raw input lines - are fed into a single SMP-aware interrupt controller on the CPU. */ -typedef struct { - SysBusDevice parent_obj; - - SysBusDevice *priv; - qemu_irq cpuic[32]; - qemu_irq rvic[4][64]; - uint32_t num_cpu; -} mpcore_rirq_state; - -/* Map baseboard IRQs onto CPU IRQ lines. */ -static const int mpcore_irq_map[32] = { - -1, -1, -1, -1, 1, 2, -1, -1, - -1, -1, 6, -1, 4, 5, -1, -1, - -1, 14, 15, 0, 7, 8, -1, -1, - -1, -1, -1, -1, 9, 3, -1, -1, -}; - -static void mpcore_rirq_set_irq(void *opaque, int irq, int level) +static void mpcore_priv_initfn(Object *obj) { - mpcore_rirq_state *s = (mpcore_rirq_state *)opaque; - int i; - - for (i = 0; i < 4; i++) { - qemu_set_irq(s->rvic[i][irq], level); - } - if (irq < 32) { - irq = mpcore_irq_map[irq]; - if (irq >= 0) { - qemu_set_irq(s->cpuic[irq], level); - } - } -} + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ARM11MPCorePriveState *s = ARM11MPCORE_PRIV(obj); -static int realview_mpcore_init(SysBusDevice *sbd) -{ - DeviceState *dev = DEVICE(sbd); - mpcore_rirq_state *s = REALVIEW_MPCORE_RIRQ(dev); - DeviceState *gic; - DeviceState *priv; - int n; - int i; + memory_region_init(&s->container, OBJECT(s), + "mpcore-priv-container", 0x2000); + sysbus_init_mmio(sbd, &s->container); - priv = qdev_create(NULL, TYPE_ARM11MPCORE_PRIV); - qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu); - qdev_init_nofail(priv); - s->priv = SYS_BUS_DEVICE(priv); - sysbus_pass_irq(sbd, s->priv); - for (i = 0; i < 32; i++) { - s->cpuic[i] = qdev_get_gpio_in(priv, i); - } - /* ??? IRQ routing is hardcoded to "normal" mode. */ - for (n = 0; n < 4; n++) { - gic = sysbus_create_simple("realview_gic", 0x10040000 + n * 0x10000, - s->cpuic[10 + n]); - for (i = 0; i < 64; i++) { - s->rvic[n][i] = qdev_get_gpio_in(gic, i); - } - } - qdev_init_gpio_in(dev, mpcore_rirq_set_irq, 64); - sysbus_init_mmio(sbd, sysbus_mmio_get_region(s->priv, 0)); - return 0; -} + object_initialize(&s->scu, sizeof(s->scu), TYPE_ARM11_SCU); + qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); -static Property mpcore_rirq_properties[] = { - DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), - DEFINE_PROP_END_OF_LIST(), -}; + object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); + qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); + /* Request the legacy 11MPCore GIC behaviour: */ + qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 0); -static void mpcore_rirq_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER); + qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default()); - k->init = realview_mpcore_init; - dc->props = mpcore_rirq_properties; + object_initialize(&s->wdtimer, sizeof(s->wdtimer), TYPE_ARM_MPTIMER); + qdev_set_parent_bus(DEVICE(&s->wdtimer), sysbus_get_default()); } -static const TypeInfo mpcore_rirq_info = { - .name = TYPE_REALVIEW_MPCORE_RIRQ, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mpcore_rirq_state), - .class_init = mpcore_rirq_class_init, -}; - static Property mpcore_priv_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARM11MPCorePriveState, num_cpu, 1), /* The ARM11 MPCORE TRM says the on-chip controller may have @@ -269,9 +151,8 @@ static Property mpcore_priv_properties[] = { static void mpcore_priv_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mpcore_priv_init; + dc->realize = mpcore_priv_realize; dc->props = mpcore_priv_properties; } @@ -279,12 +160,12 @@ static const TypeInfo mpcore_priv_info = { .name = TYPE_ARM11MPCORE_PRIV, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ARM11MPCorePriveState), + .instance_init = mpcore_priv_initfn, .class_init = mpcore_priv_class_init, }; static void arm11mpcore_register_types(void) { - type_register_static(&mpcore_rirq_info); type_register_static(&mpcore_priv_info); } diff --git a/hw/cpu/realview_mpcore.c b/hw/cpu/realview_mpcore.c new file mode 100644 index 0000000000..c39a2da42d --- /dev/null +++ b/hw/cpu/realview_mpcore.c @@ -0,0 +1,139 @@ +/* + * RealView ARM11MPCore internal peripheral emulation + * + * Copyright (c) 2006-2007 CodeSourcery. + * Copyright (c) 2013 SUSE LINUX Products GmbH + * Written by Paul Brook and Andreas Färber + * + * This code is licensed under the GPL. + */ + +#include "hw/cpu/arm11mpcore.h" +#include "hw/intc/realview_gic.h" + +#define TYPE_REALVIEW_MPCORE_RIRQ "realview_mpcore" +#define REALVIEW_MPCORE_RIRQ(obj) \ + OBJECT_CHECK(mpcore_rirq_state, (obj), TYPE_REALVIEW_MPCORE_RIRQ) + +/* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ + controllers. The output of these, plus some of the raw input lines + are fed into a single SMP-aware interrupt controller on the CPU. */ +typedef struct { + SysBusDevice parent_obj; + + qemu_irq cpuic[32]; + qemu_irq rvic[4][64]; + uint32_t num_cpu; + + ARM11MPCorePriveState priv; + RealViewGICState gic[4]; +} mpcore_rirq_state; + +/* Map baseboard IRQs onto CPU IRQ lines. */ +static const int mpcore_irq_map[32] = { + -1, -1, -1, -1, 1, 2, -1, -1, + -1, -1, 6, -1, 4, 5, -1, -1, + -1, 14, 15, 0, 7, 8, -1, -1, + -1, -1, -1, -1, 9, 3, -1, -1, +}; + +static void mpcore_rirq_set_irq(void *opaque, int irq, int level) +{ + mpcore_rirq_state *s = (mpcore_rirq_state *)opaque; + int i; + + for (i = 0; i < 4; i++) { + qemu_set_irq(s->rvic[i][irq], level); + } + if (irq < 32) { + irq = mpcore_irq_map[irq]; + if (irq >= 0) { + qemu_set_irq(s->cpuic[irq], level); + } + } +} + +static void realview_mpcore_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + mpcore_rirq_state *s = REALVIEW_MPCORE_RIRQ(dev); + DeviceState *priv = DEVICE(&s->priv); + DeviceState *gic; + SysBusDevice *gicbusdev; + Error *err = NULL; + int n; + int i; + + qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu); + object_property_set_bool(OBJECT(&s->priv), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(&s->priv)); + for (i = 0; i < 32; i++) { + s->cpuic[i] = qdev_get_gpio_in(priv, i); + } + /* ??? IRQ routing is hardcoded to "normal" mode. */ + for (n = 0; n < 4; n++) { + object_property_set_bool(OBJECT(&s->gic[n]), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + gic = DEVICE(&s->gic[n]); + gicbusdev = SYS_BUS_DEVICE(&s->gic[n]); + sysbus_mmio_map(gicbusdev, 0, 0x10040000 + n * 0x10000); + sysbus_connect_irq(gicbusdev, 0, s->cpuic[10 + n]); + for (i = 0; i < 64; i++) { + s->rvic[n][i] = qdev_get_gpio_in(gic, i); + } + } + qdev_init_gpio_in(dev, mpcore_rirq_set_irq, 64); +} + +static void mpcore_rirq_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + mpcore_rirq_state *s = REALVIEW_MPCORE_RIRQ(obj); + SysBusDevice *privbusdev; + int i; + + object_initialize(&s->priv, sizeof(s->priv), TYPE_ARM11MPCORE_PRIV); + qdev_set_parent_bus(DEVICE(&s->priv), sysbus_get_default()); + privbusdev = SYS_BUS_DEVICE(&s->priv); + sysbus_init_mmio(sbd, sysbus_mmio_get_region(privbusdev, 0)); + + for (i = 0; i < 4; i++) { + object_initialize(&s->gic[i], sizeof(s->gic[i]), TYPE_REALVIEW_GIC); + qdev_set_parent_bus(DEVICE(&s->gic[i]), sysbus_get_default()); + } +} + +static Property mpcore_rirq_properties[] = { + DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void mpcore_rirq_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = realview_mpcore_realize; + dc->props = mpcore_rirq_properties; +} + +static const TypeInfo mpcore_rirq_info = { + .name = TYPE_REALVIEW_MPCORE_RIRQ, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(mpcore_rirq_state), + .instance_init = mpcore_rirq_init, + .class_init = mpcore_rirq_class_init, +}; + +static void realview_mpcore_register_types(void) +{ + type_register_static(&mpcore_rirq_info); +} + +type_init(realview_mpcore_register_types) diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index 03058d3bc5..55240886f5 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -32,6 +32,7 @@ #include "boot.h" #include "sysemu/blockdev.h" #include "exec/address-spaces.h" +#include "sysemu/qtest.h" #define D(x) #define DNAND(x) @@ -340,14 +341,14 @@ void axisdev88_init(QEMUMachineInitArgs *args) irq[0x14 + i]); } - if (!kernel_filename) { + if (kernel_filename) { + li.image_filename = kernel_filename; + li.cmdline = kernel_cmdline; + cris_load_image(cpu, &li); + } else if (!qtest_enabled()) { fprintf(stderr, "Kernel image must be specified\n"); exit(1); } - - li.image_filename = kernel_filename; - li.cmdline = kernel_cmdline; - cris_load_image(cpu, &li); } static QEMUMachine axisdev88_machine = { diff --git a/hw/display/qxl.c b/hw/display/qxl.c index de835d6af8..efdefd6622 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -1101,7 +1101,7 @@ static void qxl_update_irq(PCIQXLDevice *d) uint32_t pending = le32_to_cpu(d->ram->int_pending); uint32_t mask = le32_to_cpu(d->ram->int_mask); int level = !!(pending & mask); - qemu_set_irq(d->pci.irq[0], level); + pci_set_irq(&d->pci, level); qxl_ring_set_dirty(d); } @@ -1701,15 +1701,9 @@ static const MemoryRegionOps qxl_io_ops = { }, }; -static void pipe_read(void *opaque) +static void qxl_update_irq_bh(void *opaque) { PCIQXLDevice *d = opaque; - char dummy; - int len; - - do { - len = read(d->pipe[0], &dummy, sizeof(dummy)); - } while (len == sizeof(dummy)); qxl_update_irq(d); } @@ -1730,28 +1724,7 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events) if ((old_pending & le_events) == le_events) { return; } - if (qemu_thread_is_self(&d->main)) { - qxl_update_irq(d); - } else { - if (write(d->pipe[1], d, 1) != 1) { - dprint(d, 1, "%s: write to pipe failed\n", __func__); - } - } -} - -static void init_pipe_signaling(PCIQXLDevice *d) -{ - if (pipe(d->pipe) < 0) { - fprintf(stderr, "%s:%s: qxl pipe creation failed\n", - __FILE__, __func__); - exit(1); - } - fcntl(d->pipe[0], F_SETFL, O_NONBLOCK); - fcntl(d->pipe[1], F_SETFL, O_NONBLOCK); - fcntl(d->pipe[0], F_SETOWN, getpid()); - - qemu_thread_get_self(&d->main); - qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d); + qemu_bh_schedule(d->update_irq); } /* graphics console */ @@ -2044,7 +2017,7 @@ static int qxl_init_common(PCIQXLDevice *qxl) } qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); - init_pipe_signaling(qxl); + qxl->update_irq = qemu_bh_new(qxl_update_irq_bh, qxl); qxl_reset_state(qxl); qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl); diff --git a/hw/display/qxl.h b/hw/display/qxl.h index 84f0182383..c5de3d7075 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -81,8 +81,7 @@ typedef struct PCIQXLDevice { QemuMutex track_lock; /* thread signaling */ - QemuThread main; - int pipe[2]; + QEMUBH *update_irq; /* ram pci bar */ QXLRam *ram; diff --git a/hw/display/vga.c b/hw/display/vga.c index b5e22849ab..063319d34d 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1707,7 +1707,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } else if (is_buffer_shared(surface) && (full_update || surface_data(surface) != s->vram_ptr + (s->start_addr * 4))) { - DisplaySurface *surface; surface = qemu_create_displaysurface_from(disp_width, height, depth, s->line_offset, s->vram_ptr + (s->start_addr * 4), byteswap); diff --git a/hw/display/vga_template.h b/hw/display/vga_template.h index f6f6a01d84..6cfae567b4 100644 --- a/hw/display/vga_template.h +++ b/hw/display/vga_template.h @@ -113,20 +113,22 @@ static void glue(vga_draw_glyph9_, DEPTH)(uint8_t *d, int linesize, do { font_data = font_ptr[0]; #if BPP == 1 - cpu_to_32wu((uint32_t *)d, (dmask16[(font_data >> 4)] & xorcol) ^ bgcol); + stl_p((uint32_t *)d, (dmask16[(font_data >> 4)] & xorcol) ^ bgcol); v = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol; - cpu_to_32wu(((uint32_t *)d)+1, v); + stl_p(((uint32_t *)d)+1, v); if (dup9) ((uint8_t *)d)[8] = v >> (24 * (1 - BIG)); else ((uint8_t *)d)[8] = bgcol; #elif BPP == 2 - cpu_to_32wu(((uint32_t *)d)+0, (dmask4[(font_data >> 6)] & xorcol) ^ bgcol); - cpu_to_32wu(((uint32_t *)d)+1, (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol); - cpu_to_32wu(((uint32_t *)d)+2, (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol); + stl_p(((uint32_t *)d)+0, (dmask4[(font_data >> 6)] & xorcol) ^ bgcol); + stl_p(((uint32_t *)d)+1, + (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol); + stl_p(((uint32_t *)d)+2, + (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol); v = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol; - cpu_to_32wu(((uint32_t *)d)+3, v); + stl_p(((uint32_t *)d)+3, v); if (dup9) ((uint16_t *)d)[8] = v >> (16 * (1 - BIG)); else diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index 45e61655e9..09ac433cf9 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -5,3 +5,30 @@ obj-y += pc_sysfw.o obj-$(CONFIG_XEN) += xen_domainbuild.o xen_machine_pv.o obj-y += kvmvapic.o +obj-y += acpi-build.o +obj-y += bios-linker-loader.o +hw/i386/acpi-build.o: hw/i386/acpi-build.c hw/i386/acpi-dsdt.hex \ + hw/i386/ssdt-proc.hex hw/i386/ssdt-pcihp.hex hw/i386/ssdt-misc.hex \ + hw/i386/acpi-dsdt.hex hw/i386/q35-acpi-dsdt.hex + +iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ + ; then echo "$(2)"; else echo "$(3)"; fi ;) + +ifdef IASL +#IASL Present. Generate hex files from .dsl +hw/i386/%.hex: $(SRC_PATH)/hw/i386/%.dsl $(SRC_PATH)/scripts/acpi_extract_preprocess.py $(SRC_PATH)/scripts/acpi_extract.py + $(call quiet-command, cpp -P $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig") + $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract_preprocess.py $*.dsl.i.orig > $*.dsl.i, " ACPI_PREPROCESS $(TARGET_DIR)$*.dsl.i") + $(call quiet-command, $(IASL) $(call iasl-option,$(IASL),-Pn,) -vs -l -tc -p $* $*.dsl.i $(if $(V), , > /dev/null) 2>&1 ," IASL $(TARGET_DIR)$*.dsl.i") + $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract.py $*.lst > $*.off, " ACPI_EXTRACT $(TARGET_DIR)$*.off") + $(call quiet-command, cat $*.off > $@, " CAT $(TARGET_DIR)$@") +else +#IASL Not present. Restore pre-generated hex files. +hw/i386/%.hex: $(SRC_PATH)/hw/i386/%.hex.generated + $(call quiet-command, cp -f $< $@, " CP $(TARGET_DIR)$@") +endif + +.PHONY: cleanhex +cleanhex: + rm -f hw/i386/*hex +clean: cleanhex diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c new file mode 100644 index 0000000000..486e7055a6 --- /dev/null +++ b/hw/i386/acpi-build.c @@ -0,0 +1,1219 @@ +/* Support for generating ACPI tables and passing them to Guests + * + * Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2013 Red Hat Inc + * + * Author: Michael S. Tsirkin <mst@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "acpi-build.h" +#include <stddef.h> +#include <glib.h> +#include "qemu-common.h" +#include "qemu/bitmap.h" +#include "qemu/range.h" +#include "hw/pci/pci.h" +#include "qom/cpu.h" +#include "hw/i386/pc.h" +#include "target-i386/cpu.h" +#include "hw/timer/hpet.h" +#include "hw/i386/acpi-defs.h" +#include "hw/acpi/acpi.h" +#include "hw/nvram/fw_cfg.h" +#include "bios-linker-loader.h" +#include "hw/loader.h" + +/* Supported chipsets: */ +#include "hw/acpi/piix4.h" +#include "hw/i386/ich9.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci-host/q35.h" + +#include "hw/i386/q35-acpi-dsdt.hex" +#include "hw/i386/acpi-dsdt.hex" + +#include "qapi/qmp/qint.h" +#include "qom/qom-qobject.h" + +typedef struct AcpiCpuInfo { + DECLARE_BITMAP(found_cpus, MAX_CPUMASK_BITS + 1); +} AcpiCpuInfo; + +typedef struct AcpiMcfgInfo { + uint64_t mcfg_base; + uint32_t mcfg_size; +} AcpiMcfgInfo; + +typedef struct AcpiPmInfo { + bool s3_disabled; + bool s4_disabled; + uint8_t s4_val; + uint16_t sci_int; + uint8_t acpi_enable_cmd; + uint8_t acpi_disable_cmd; + uint32_t gpe0_blk; + uint32_t gpe0_blk_len; + uint32_t io_base; +} AcpiPmInfo; + +typedef struct AcpiMiscInfo { + bool has_hpet; + DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX); + const unsigned char *dsdt_code; + unsigned dsdt_size; + uint16_t pvpanic_port; +} AcpiMiscInfo; + +static void acpi_get_dsdt(AcpiMiscInfo *info) +{ + Object *piix = piix4_pm_find(); + Object *lpc = ich9_lpc_find(); + assert(!!piix != !!lpc); + + if (piix) { + info->dsdt_code = AcpiDsdtAmlCode; + info->dsdt_size = sizeof AcpiDsdtAmlCode; + } + if (lpc) { + info->dsdt_code = Q35AcpiDsdtAmlCode; + info->dsdt_size = sizeof Q35AcpiDsdtAmlCode; + } +} + +static +int acpi_add_cpu_info(Object *o, void *opaque) +{ + AcpiCpuInfo *cpu = opaque; + uint64_t apic_id; + + if (object_dynamic_cast(o, TYPE_CPU)) { + apic_id = object_property_get_int(o, "apic-id", NULL); + assert(apic_id <= MAX_CPUMASK_BITS); + + set_bit(apic_id, cpu->found_cpus); + } + + object_child_foreach(o, acpi_add_cpu_info, opaque); + return 0; +} + +static void acpi_get_cpu_info(AcpiCpuInfo *cpu) +{ + Object *root = object_get_root(); + + memset(cpu->found_cpus, 0, sizeof cpu->found_cpus); + object_child_foreach(root, acpi_add_cpu_info, cpu); +} + +static void acpi_get_pm_info(AcpiPmInfo *pm) +{ + Object *piix = piix4_pm_find(); + Object *lpc = ich9_lpc_find(); + Object *obj = NULL; + QObject *o; + + if (piix) { + obj = piix; + } + if (lpc) { + obj = lpc; + } + assert(obj); + + /* Fill in optional s3/s4 related properties */ + o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL); + if (o) { + pm->s3_disabled = qint_get_int(qobject_to_qint(o)); + } else { + pm->s3_disabled = false; + } + o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_DISABLED, NULL); + if (o) { + pm->s4_disabled = qint_get_int(qobject_to_qint(o)); + } else { + pm->s4_disabled = false; + } + o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_VAL, NULL); + if (o) { + pm->s4_val = qint_get_int(qobject_to_qint(o)); + } else { + pm->s4_val = false; + } + + /* Fill in mandatory properties */ + pm->sci_int = object_property_get_int(obj, ACPI_PM_PROP_SCI_INT, NULL); + + pm->acpi_enable_cmd = object_property_get_int(obj, + ACPI_PM_PROP_ACPI_ENABLE_CMD, + NULL); + pm->acpi_disable_cmd = object_property_get_int(obj, + ACPI_PM_PROP_ACPI_DISABLE_CMD, + NULL); + pm->io_base = object_property_get_int(obj, ACPI_PM_PROP_PM_IO_BASE, + NULL); + pm->gpe0_blk = object_property_get_int(obj, ACPI_PM_PROP_GPE0_BLK, + NULL); + pm->gpe0_blk_len = object_property_get_int(obj, ACPI_PM_PROP_GPE0_BLK_LEN, + NULL); +} + +static void acpi_get_hotplug_info(AcpiMiscInfo *misc) +{ + int i; + PCIBus *bus = find_i440fx(); + + if (!bus) { + /* Only PIIX supports ACPI hotplug */ + memset(misc->slot_hotplug_enable, 0, sizeof misc->slot_hotplug_enable); + return; + } + + memset(misc->slot_hotplug_enable, 0xff, + DIV_ROUND_UP(PCI_SLOT_MAX, BITS_PER_BYTE)); + + for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + PCIDeviceClass *pc; + PCIDevice *pdev = bus->devices[i]; + + if (!pdev) { + continue; + } + + pc = PCI_DEVICE_GET_CLASS(pdev); + + if (pc->no_hotplug) { + int slot = PCI_SLOT(i); + + clear_bit(slot, misc->slot_hotplug_enable); + } + } +} + +static void acpi_get_misc_info(AcpiMiscInfo *info) +{ + info->has_hpet = hpet_find(); + info->pvpanic_port = pvpanic_port(); +} + +static void acpi_get_pci_info(PcPciInfo *info) +{ + Object *pci_host; + bool ambiguous; + + pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous); + g_assert(!ambiguous); + g_assert(pci_host); + + info->w32.begin = object_property_get_int(pci_host, + PCI_HOST_PROP_PCI_HOLE_START, + NULL); + info->w32.end = object_property_get_int(pci_host, + PCI_HOST_PROP_PCI_HOLE_END, + NULL); + info->w64.begin = object_property_get_int(pci_host, + PCI_HOST_PROP_PCI_HOLE64_START, + NULL); + info->w64.end = object_property_get_int(pci_host, + PCI_HOST_PROP_PCI_HOLE64_END, + NULL); +} + +#define ACPI_BUILD_APPNAME "Bochs" +#define ACPI_BUILD_APPNAME6 "BOCHS " +#define ACPI_BUILD_APPNAME4 "BXPC" + +#define ACPI_BUILD_DPRINTF(level, fmt, ...) do {} while (0) + +#define ACPI_BUILD_TABLE_FILE "etc/acpi/tables" +#define ACPI_BUILD_RSDP_FILE "etc/acpi/rsdp" + +static void +build_header(GArray *linker, GArray *table_data, + AcpiTableHeader *h, uint32_t sig, int len, uint8_t rev) +{ + h->signature = cpu_to_le32(sig); + h->length = cpu_to_le32(len); + h->revision = rev; + memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6); + memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4); + memcpy(h->oem_table_id + 4, (void *)&sig, 4); + h->oem_revision = cpu_to_le32(1); + memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4); + h->asl_compiler_revision = cpu_to_le32(1); + h->checksum = 0; + /* Checksum to be filled in by Guest linker */ + bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE, + table_data->data, h, len, &h->checksum); +} + +static inline GArray *build_alloc_array(void) +{ + return g_array_new(false, true /* clear */, 1); +} + +static inline void build_free_array(GArray *array) +{ + g_array_free(array, true); +} + +static inline void build_prepend_byte(GArray *array, uint8_t val) +{ + g_array_prepend_val(array, val); +} + +static inline void build_append_byte(GArray *array, uint8_t val) +{ + g_array_append_val(array, val); +} + +static inline void build_append_array(GArray *array, GArray *val) +{ + g_array_append_vals(array, val->data, val->len); +} + +static void build_append_nameseg(GArray *array, const char *format, ...) +{ + GString *s = g_string_new(""); + va_list args; + + va_start(args, format); + g_string_vprintf(s, format, args); + va_end(args); + + assert(s->len == 4); + g_array_append_vals(array, s->str, s->len); + g_string_free(s, true); +} + +/* 5.4 Definition Block Encoding */ +enum { + PACKAGE_LENGTH_1BYTE_SHIFT = 6, /* Up to 63 - use extra 2 bits. */ + PACKAGE_LENGTH_2BYTE_SHIFT = 4, + PACKAGE_LENGTH_3BYTE_SHIFT = 12, + PACKAGE_LENGTH_4BYTE_SHIFT = 20, +}; + +static void build_prepend_package_length(GArray *package, unsigned min_bytes) +{ + uint8_t byte; + unsigned length = package->len; + unsigned length_bytes; + + if (length + 1 < (1 << PACKAGE_LENGTH_1BYTE_SHIFT)) { + length_bytes = 1; + } else if (length + 2 < (1 << PACKAGE_LENGTH_3BYTE_SHIFT)) { + length_bytes = 2; + } else if (length + 3 < (1 << PACKAGE_LENGTH_4BYTE_SHIFT)) { + length_bytes = 3; + } else { + length_bytes = 4; + } + + /* Force length to at least min_bytes. + * This wastes memory but that's how bios did it. + */ + length_bytes = MAX(length_bytes, min_bytes); + + /* PkgLength is the length of the inclusive length of the data. */ + length += length_bytes; + + switch (length_bytes) { + case 1: + byte = length; + build_prepend_byte(package, byte); + return; + case 4: + byte = length >> PACKAGE_LENGTH_4BYTE_SHIFT; + build_prepend_byte(package, byte); + length &= (1 << PACKAGE_LENGTH_4BYTE_SHIFT) - 1; + /* fall through */ + case 3: + byte = length >> PACKAGE_LENGTH_3BYTE_SHIFT; + build_prepend_byte(package, byte); + length &= (1 << PACKAGE_LENGTH_3BYTE_SHIFT) - 1; + /* fall through */ + case 2: + byte = length >> PACKAGE_LENGTH_2BYTE_SHIFT; + build_prepend_byte(package, byte); + length &= (1 << PACKAGE_LENGTH_2BYTE_SHIFT) - 1; + /* fall through */ + } + /* + * Most significant two bits of byte zero indicate how many following bytes + * are in PkgLength encoding. + */ + byte = ((length_bytes - 1) << PACKAGE_LENGTH_1BYTE_SHIFT) | length; + build_prepend_byte(package, byte); +} + +static void build_package(GArray *package, uint8_t op, unsigned min_bytes) +{ + build_prepend_package_length(package, min_bytes); + build_prepend_byte(package, op); +} + +static void build_append_value(GArray *table, uint32_t value, int size) +{ + uint8_t prefix; + int i; + + switch (size) { + case 1: + prefix = 0x0A; /* BytePrefix */ + break; + case 2: + prefix = 0x0B; /* WordPrefix */ + break; + case 4: + prefix = 0x0C; /* DWordPrefix */ + break; + default: + assert(0); + return; + } + build_append_byte(table, prefix); + for (i = 0; i < size; ++i) { + build_append_byte(table, value & 0xFF); + value = value >> 8; + } +} + +static void build_append_notify_target(GArray *method, GArray *target_name, + uint32_t value, int size) +{ + GArray *notify = build_alloc_array(); + uint8_t op = 0xA0; /* IfOp */ + + build_append_byte(notify, 0x93); /* LEqualOp */ + build_append_byte(notify, 0x68); /* Arg0Op */ + build_append_value(notify, value, size); + build_append_byte(notify, 0x86); /* NotifyOp */ + build_append_array(notify, target_name); + build_append_byte(notify, 0x69); /* Arg1Op */ + + /* Pack it up */ + build_package(notify, op, 1); + + build_append_array(method, notify); + + build_free_array(notify); +} + +#define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */ + +static inline void *acpi_data_push(GArray *table_data, unsigned size) +{ + unsigned off = table_data->len; + g_array_set_size(table_data, off + size); + return table_data->data + off; +} + +static unsigned acpi_data_len(GArray *table) +{ + return table->len * g_array_get_element_size(table); +} + +static void acpi_align_size(GArray *blob, unsigned align) +{ + /* Align size to multiple of given size. This reduces the chance + * we need to change size in the future (breaking cross version migration). + */ + g_array_set_size(blob, (ROUND_UP(acpi_data_len(blob), align) + + g_array_get_element_size(blob) - 1) / + g_array_get_element_size(blob)); +} + +/* Get pointer within table in a safe manner */ +#define ACPI_BUILD_PTR(table, size, off, type) \ + ((type *)(acpi_data_get_ptr(table, size, off, sizeof(type)))) + +static inline void *acpi_data_get_ptr(uint8_t *table_data, unsigned table_size, + unsigned off, unsigned size) +{ + assert(off + size > off); + assert(off + size <= table_size); + return table_data + off; +} + +static inline void acpi_add_table(GArray *table_offsets, GArray *table_data) +{ + uint32_t offset = cpu_to_le32(table_data->len); + g_array_append_val(table_offsets, offset); +} + +/* FACS */ +static void +build_facs(GArray *table_data, GArray *linker, PcGuestInfo *guest_info) +{ + AcpiFacsDescriptorRev1 *facs = acpi_data_push(table_data, sizeof *facs); + facs->signature = cpu_to_le32(ACPI_FACS_SIGNATURE); + facs->length = cpu_to_le32(sizeof(*facs)); +} + +/* Load chipset information in FADT */ +static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm) +{ + fadt->model = 1; + fadt->reserved1 = 0; + fadt->sci_int = cpu_to_le16(pm->sci_int); + fadt->smi_cmd = cpu_to_le32(ACPI_PORT_SMI_CMD); + fadt->acpi_enable = pm->acpi_enable_cmd; + fadt->acpi_disable = pm->acpi_disable_cmd; + /* EVT, CNT, TMR offset matches hw/acpi/core.c */ + fadt->pm1a_evt_blk = cpu_to_le32(pm->io_base); + fadt->pm1a_cnt_blk = cpu_to_le32(pm->io_base + 0x04); + fadt->pm_tmr_blk = cpu_to_le32(pm->io_base + 0x08); + fadt->gpe0_blk = cpu_to_le32(pm->gpe0_blk); + /* EVT, CNT, TMR length matches hw/acpi/core.c */ + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + fadt->pm_tmr_len = 4; + fadt->gpe0_blk_len = pm->gpe0_blk_len; + fadt->plvl2_lat = cpu_to_le16(0xfff); /* C2 state not supported */ + fadt->plvl3_lat = cpu_to_le16(0xfff); /* C3 state not supported */ + fadt->flags = cpu_to_le32((1 << ACPI_FADT_F_WBINVD) | + (1 << ACPI_FADT_F_PROC_C1) | + (1 << ACPI_FADT_F_SLP_BUTTON) | + (1 << ACPI_FADT_F_RTC_S4)); + fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_USE_PLATFORM_CLOCK); +} + + +/* FADT */ +static void +build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm, + unsigned facs, unsigned dsdt) +{ + AcpiFadtDescriptorRev1 *fadt = acpi_data_push(table_data, sizeof(*fadt)); + + fadt->firmware_ctrl = cpu_to_le32(facs); + /* FACS address to be filled by Guest linker */ + bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, + ACPI_BUILD_TABLE_FILE, + table_data, &fadt->firmware_ctrl, + sizeof fadt->firmware_ctrl); + + fadt->dsdt = cpu_to_le32(dsdt); + /* DSDT address to be filled by Guest linker */ + bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, + ACPI_BUILD_TABLE_FILE, + table_data, &fadt->dsdt, + sizeof fadt->dsdt); + + fadt_setup(fadt, pm); + + build_header(linker, table_data, + (void *)fadt, ACPI_FACP_SIGNATURE, sizeof(*fadt), 1); +} + +static void +build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, + PcGuestInfo *guest_info) +{ + int madt_start = table_data->len; + + AcpiMultipleApicTable *madt; + AcpiMadtIoApic *io_apic; + AcpiMadtIntsrcovr *intsrcovr; + AcpiMadtLocalNmi *local_nmi; + int i; + + madt = acpi_data_push(table_data, sizeof *madt); + madt->local_apic_address = cpu_to_le32(APIC_DEFAULT_ADDRESS); + madt->flags = cpu_to_le32(1); + + for (i = 0; i < guest_info->apic_id_limit; i++) { + AcpiMadtProcessorApic *apic = acpi_data_push(table_data, sizeof *apic); + apic->type = ACPI_APIC_PROCESSOR; + apic->length = sizeof(*apic); + apic->processor_id = i; + apic->local_apic_id = i; + if (test_bit(i, cpu->found_cpus)) { + apic->flags = cpu_to_le32(1); + } else { + apic->flags = cpu_to_le32(0); + } + } + io_apic = acpi_data_push(table_data, sizeof *io_apic); + io_apic->type = ACPI_APIC_IO; + io_apic->length = sizeof(*io_apic); +#define ACPI_BUILD_IOAPIC_ID 0x0 + io_apic->io_apic_id = ACPI_BUILD_IOAPIC_ID; + io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS); + io_apic->interrupt = cpu_to_le32(0); + + if (guest_info->apic_xrupt_override) { + intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); + intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; + intsrcovr->length = sizeof(*intsrcovr); + intsrcovr->source = 0; + intsrcovr->gsi = cpu_to_le32(2); + intsrcovr->flags = cpu_to_le16(0); /* conforms to bus specifications */ + } + for (i = 1; i < 16; i++) { +#define ACPI_BUILD_PCI_IRQS ((1<<5) | (1<<9) | (1<<10) | (1<<11)) + if (!(ACPI_BUILD_PCI_IRQS & (1 << i))) { + /* No need for a INT source override structure. */ + continue; + } + intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); + intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; + intsrcovr->length = sizeof(*intsrcovr); + intsrcovr->source = i; + intsrcovr->gsi = cpu_to_le32(i); + intsrcovr->flags = cpu_to_le16(0xd); /* active high, level triggered */ + } + + local_nmi = acpi_data_push(table_data, sizeof *local_nmi); + local_nmi->type = ACPI_APIC_LOCAL_NMI; + local_nmi->length = sizeof(*local_nmi); + local_nmi->processor_id = 0xff; /* all processors */ + local_nmi->flags = cpu_to_le16(0); + local_nmi->lint = 1; /* ACPI_LINT1 */ + + build_header(linker, table_data, + (void *)(table_data->data + madt_start), ACPI_APIC_SIGNATURE, + table_data->len - madt_start, 1); +} + +/* Encode a hex value */ +static inline char acpi_get_hex(uint32_t val) +{ + val &= 0x0f; + return (val <= 9) ? ('0' + val) : ('A' + val - 10); +} + +#include "hw/i386/ssdt-proc.hex" + +/* 0x5B 0x83 ProcessorOp PkgLength NameString ProcID */ +#define ACPI_PROC_OFFSET_CPUHEX (*ssdt_proc_name - *ssdt_proc_start + 2) +#define ACPI_PROC_OFFSET_CPUID1 (*ssdt_proc_name - *ssdt_proc_start + 4) +#define ACPI_PROC_OFFSET_CPUID2 (*ssdt_proc_id - *ssdt_proc_start) +#define ACPI_PROC_SIZEOF (*ssdt_proc_end - *ssdt_proc_start) +#define ACPI_PROC_AML (ssdp_proc_aml + *ssdt_proc_start) + +/* 0x5B 0x82 DeviceOp PkgLength NameString */ +#define ACPI_PCIHP_OFFSET_HEX (*ssdt_pcihp_name - *ssdt_pcihp_start + 1) +#define ACPI_PCIHP_OFFSET_ID (*ssdt_pcihp_id - *ssdt_pcihp_start) +#define ACPI_PCIHP_OFFSET_ADR (*ssdt_pcihp_adr - *ssdt_pcihp_start) +#define ACPI_PCIHP_OFFSET_EJ0 (*ssdt_pcihp_ej0 - *ssdt_pcihp_start) +#define ACPI_PCIHP_SIZEOF (*ssdt_pcihp_end - *ssdt_pcihp_start) +#define ACPI_PCIHP_AML (ssdp_pcihp_aml + *ssdt_pcihp_start) + +#define ACPI_SSDT_SIGNATURE 0x54445353 /* SSDT */ +#define ACPI_SSDT_HEADER_LENGTH 36 + +#include "hw/i386/ssdt-misc.hex" +#include "hw/i386/ssdt-pcihp.hex" + +static void +build_append_notify(GArray *device, const char *name, + const char *format, int skip, int count) +{ + int i; + GArray *method = build_alloc_array(); + uint8_t op = 0x14; /* MethodOp */ + + build_append_nameseg(method, name); + build_append_byte(method, 0x02); /* MethodFlags: ArgCount */ + for (i = skip; i < count; i++) { + GArray *target = build_alloc_array(); + build_append_nameseg(target, format, i); + assert(i < 256); /* Fits in 1 byte */ + build_append_notify_target(method, target, i, 1); + build_free_array(target); + } + build_package(method, op, 2); + + build_append_array(device, method); + build_free_array(method); +} + +static void patch_pcihp(int slot, uint8_t *ssdt_ptr, uint32_t eject) +{ + ssdt_ptr[ACPI_PCIHP_OFFSET_HEX] = acpi_get_hex(slot >> 4); + ssdt_ptr[ACPI_PCIHP_OFFSET_HEX + 1] = acpi_get_hex(slot); + ssdt_ptr[ACPI_PCIHP_OFFSET_ID] = slot; + ssdt_ptr[ACPI_PCIHP_OFFSET_ADR + 2] = slot; + + /* Runtime patching of ACPI_EJ0: to disable hotplug for a slot, + * replace the method name: _EJ0 by ACPI_EJ0_. + */ + /* Sanity check */ + assert(!memcmp(ssdt_ptr + ACPI_PCIHP_OFFSET_EJ0, "_EJ0", 4)); + + if (!eject) { + memcpy(ssdt_ptr + ACPI_PCIHP_OFFSET_EJ0, "EJ0_", 4); + } +} + +static void patch_pci_windows(PcPciInfo *pci, uint8_t *start, unsigned size) +{ + *ACPI_BUILD_PTR(start, size, acpi_pci32_start[0], uint32_t) = + cpu_to_le32(pci->w32.begin); + + *ACPI_BUILD_PTR(start, size, acpi_pci32_end[0], uint32_t) = + cpu_to_le32(pci->w32.end - 1); + + if (pci->w64.end || pci->w64.begin) { + *ACPI_BUILD_PTR(start, size, acpi_pci64_valid[0], uint8_t) = 1; + *ACPI_BUILD_PTR(start, size, acpi_pci64_start[0], uint64_t) = + cpu_to_le64(pci->w64.begin); + *ACPI_BUILD_PTR(start, size, acpi_pci64_end[0], uint64_t) = + cpu_to_le64(pci->w64.end - 1); + *ACPI_BUILD_PTR(start, size, acpi_pci64_length[0], uint64_t) = + cpu_to_le64(pci->w64.end - pci->w64.begin); + } else { + *ACPI_BUILD_PTR(start, size, acpi_pci64_valid[0], uint8_t) = 0; + } +} + +static void +build_ssdt(GArray *table_data, GArray *linker, + AcpiCpuInfo *cpu, AcpiPmInfo *pm, AcpiMiscInfo *misc, + PcPciInfo *pci, PcGuestInfo *guest_info) +{ + int acpi_cpus = MIN(0xff, guest_info->apic_id_limit); + int ssdt_start = table_data->len; + uint8_t *ssdt_ptr; + int i; + + /* Copy header and patch values in the S3_ / S4_ / S5_ packages */ + ssdt_ptr = acpi_data_push(table_data, sizeof(ssdp_misc_aml)); + memcpy(ssdt_ptr, ssdp_misc_aml, sizeof(ssdp_misc_aml)); + if (pm->s3_disabled) { + ssdt_ptr[acpi_s3_name[0]] = 'X'; + } + if (pm->s4_disabled) { + ssdt_ptr[acpi_s4_name[0]] = 'X'; + } else { + ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt_ptr[acpi_s4_pkg[0] + 3] = + pm->s4_val; + } + + patch_pci_windows(pci, ssdt_ptr, sizeof(ssdp_misc_aml)); + + *(uint16_t *)(ssdt_ptr + *ssdt_isa_pest) = + cpu_to_le16(misc->pvpanic_port); + + { + GArray *sb_scope = build_alloc_array(); + uint8_t op = 0x10; /* ScopeOp */ + + build_append_nameseg(sb_scope, "_SB_"); + + /* build Processor object for each processor */ + for (i = 0; i < acpi_cpus; i++) { + uint8_t *proc = acpi_data_push(sb_scope, ACPI_PROC_SIZEOF); + memcpy(proc, ACPI_PROC_AML, ACPI_PROC_SIZEOF); + proc[ACPI_PROC_OFFSET_CPUHEX] = acpi_get_hex(i >> 4); + proc[ACPI_PROC_OFFSET_CPUHEX+1] = acpi_get_hex(i); + proc[ACPI_PROC_OFFSET_CPUID1] = i; + proc[ACPI_PROC_OFFSET_CPUID2] = i; + } + + /* build this code: + * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...} + */ + /* Arg0 = Processor ID = APIC ID */ + build_append_notify(sb_scope, "NTFY", "CP%0.02X", 0, acpi_cpus); + + /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" */ + build_append_byte(sb_scope, 0x08); /* NameOp */ + build_append_nameseg(sb_scope, "CPON"); + + { + GArray *package = build_alloc_array(); + uint8_t op = 0x12; /* PackageOp */ + + build_append_byte(package, acpi_cpus); /* NumElements */ + for (i = 0; i < acpi_cpus; i++) { + uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00; + build_append_byte(package, b); + } + + build_package(package, op, 2); + build_append_array(sb_scope, package); + build_free_array(package); + } + + { + GArray *pci0 = build_alloc_array(); + uint8_t op = 0x10; /* ScopeOp */; + + build_append_nameseg(pci0, "PCI0"); + + /* build Device object for each slot */ + for (i = 1; i < PCI_SLOT_MAX; i++) { + bool eject = test_bit(i, misc->slot_hotplug_enable); + void *pcihp = acpi_data_push(pci0, ACPI_PCIHP_SIZEOF); + + memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF); + patch_pcihp(i, pcihp, eject); + } + + build_append_notify(pci0, "PCNT", "S%0.02X_", 1, PCI_SLOT_MAX); + build_package(pci0, op, 3); + build_append_array(sb_scope, pci0); + build_free_array(pci0); + } + + build_package(sb_scope, op, 3); + build_append_array(table_data, sb_scope); + build_free_array(sb_scope); + } + + build_header(linker, table_data, + (void *)(table_data->data + ssdt_start), + ACPI_SSDT_SIGNATURE, table_data->len - ssdt_start, 1); +} + +static void +build_hpet(GArray *table_data, GArray *linker) +{ + Acpi20Hpet *hpet; + + hpet = acpi_data_push(table_data, sizeof(*hpet)); + /* Note timer_block_id value must be kept in sync with value advertised by + * emulated hpet + */ + hpet->timer_block_id = cpu_to_le32(0x8086a201); + hpet->addr.address = cpu_to_le64(HPET_BASE); + build_header(linker, table_data, + (void *)hpet, ACPI_HPET_SIGNATURE, sizeof(*hpet), 1); +} + +static void +acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, + uint64_t base, uint64_t len, int node, int enabled) +{ + numamem->type = ACPI_SRAT_MEMORY; + numamem->length = sizeof(*numamem); + memset(numamem->proximity, 0, 4); + numamem->proximity[0] = node; + numamem->flags = cpu_to_le32(!!enabled); + numamem->base_addr = cpu_to_le64(base); + numamem->range_length = cpu_to_le64(len); +} + +static void +build_srat(GArray *table_data, GArray *linker, + AcpiCpuInfo *cpu, PcGuestInfo *guest_info) +{ + AcpiSystemResourceAffinityTable *srat; + AcpiSratProcessorAffinity *core; + AcpiSratMemoryAffinity *numamem; + + int i; + uint64_t curnode; + int srat_start, numa_start, slots; + uint64_t mem_len, mem_base, next_base; + + srat_start = table_data->len; + + srat = acpi_data_push(table_data, sizeof *srat); + srat->reserved1 = cpu_to_le32(1); + core = (void *)(srat + 1); + + for (i = 0; i < guest_info->apic_id_limit; ++i) { + core = acpi_data_push(table_data, sizeof *core); + core->type = ACPI_SRAT_PROCESSOR; + core->length = sizeof(*core); + core->local_apic_id = i; + curnode = guest_info->node_cpu[i]; + core->proximity_lo = curnode; + memset(core->proximity_hi, 0, 3); + core->local_sapic_eid = 0; + if (test_bit(i, cpu->found_cpus)) { + core->flags = cpu_to_le32(1); + } else { + core->flags = cpu_to_le32(0); + } + } + + + /* the memory map is a bit tricky, it contains at least one hole + * from 640k-1M and possibly another one from 3.5G-4G. + */ + next_base = 0; + numa_start = table_data->len; + + numamem = acpi_data_push(table_data, sizeof *numamem); + acpi_build_srat_memory(numamem, 0, 640*1024, 0, 1); + next_base = 1024 * 1024; + for (i = 1; i < guest_info->numa_nodes + 1; ++i) { + mem_base = next_base; + mem_len = guest_info->node_mem[i - 1]; + if (i == 1) { + mem_len -= 1024 * 1024; + } + next_base = mem_base + mem_len; + + /* Cut out the ACPI_PCI hole */ + if (mem_base <= guest_info->ram_size && + next_base > guest_info->ram_size) { + mem_len -= next_base - guest_info->ram_size; + if (mem_len > 0) { + numamem = acpi_data_push(table_data, sizeof *numamem); + acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); + } + mem_base = 1ULL << 32; + mem_len = next_base - guest_info->ram_size; + next_base += (1ULL << 32) - guest_info->ram_size; + } + numamem = acpi_data_push(table_data, sizeof *numamem); + acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, 1); + } + slots = (table_data->len - numa_start) / sizeof *numamem; + for (; slots < guest_info->numa_nodes + 2; slots++) { + numamem = acpi_data_push(table_data, sizeof *numamem); + acpi_build_srat_memory(numamem, 0, 0, 0, 0); + } + + build_header(linker, table_data, + (void *)(table_data->data + srat_start), + ACPI_SRAT_SIGNATURE, + table_data->len - srat_start, 1); +} + +static void +build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info) +{ + AcpiTableMcfg *mcfg; + uint32_t sig; + int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]); + + mcfg = acpi_data_push(table_data, len); + mcfg->allocation[0].address = cpu_to_le64(info->mcfg_base); + /* Only a single allocation so no need to play with segments */ + mcfg->allocation[0].pci_segment = cpu_to_le16(0); + mcfg->allocation[0].start_bus_number = 0; + mcfg->allocation[0].end_bus_number = PCIE_MMCFG_BUS(info->mcfg_size - 1); + + /* MCFG is used for ECAM which can be enabled or disabled by guest. + * To avoid table size changes (which create migration issues), + * always create the table even if there are no allocations, + * but set the signature to a reserved value in this case. + * ACPI spec requires OSPMs to ignore such tables. + */ + if (info->mcfg_base == PCIE_BASE_ADDR_UNMAPPED) { + sig = ACPI_RSRV_SIGNATURE; + } else { + sig = ACPI_MCFG_SIGNATURE; + } + build_header(linker, table_data, (void *)mcfg, sig, len, 1); +} + +static void +build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc) +{ + void *dsdt; + assert(misc->dsdt_code && misc->dsdt_size); + dsdt = acpi_data_push(table_data, misc->dsdt_size); + memcpy(dsdt, misc->dsdt_code, misc->dsdt_size); +} + +/* Build final rsdt table */ +static void +build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets) +{ + AcpiRsdtDescriptorRev1 *rsdt; + size_t rsdt_len; + int i; + + rsdt_len = sizeof(*rsdt) + sizeof(uint32_t) * table_offsets->len; + rsdt = acpi_data_push(table_data, rsdt_len); + memcpy(rsdt->table_offset_entry, table_offsets->data, + sizeof(uint32_t) * table_offsets->len); + for (i = 0; i < table_offsets->len; ++i) { + /* rsdt->table_offset_entry to be filled by Guest linker */ + bios_linker_loader_add_pointer(linker, + ACPI_BUILD_TABLE_FILE, + ACPI_BUILD_TABLE_FILE, + table_data, &rsdt->table_offset_entry[i], + sizeof(uint32_t)); + } + build_header(linker, table_data, + (void *)rsdt, ACPI_RSDT_SIGNATURE, rsdt_len, 1); +} + +static GArray * +build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) +{ + AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp); + + bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, 1, + true /* fseg memory */); + + rsdp->signature = cpu_to_le64(ACPI_RSDP_SIGNATURE); + memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, 6); + rsdp->rsdt_physical_address = cpu_to_le32(rsdt); + /* Address to be filled by Guest linker */ + bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE, + ACPI_BUILD_TABLE_FILE, + rsdp_table, &rsdp->rsdt_physical_address, + sizeof rsdp->rsdt_physical_address); + rsdp->checksum = 0; + /* Checksum to be filled by Guest linker */ + bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, + rsdp, rsdp, sizeof *rsdp, &rsdp->checksum); + + return rsdp_table; +} + +typedef +struct AcpiBuildTables { + GArray *table_data; + GArray *rsdp; + GArray *linker; +} AcpiBuildTables; + +static inline void acpi_build_tables_init(AcpiBuildTables *tables) +{ + tables->rsdp = g_array_new(false, true /* clear */, 1); + tables->table_data = g_array_new(false, true /* clear */, 1); + tables->linker = bios_linker_loader_init(); +} + +static inline void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre) +{ + void *linker_data = bios_linker_loader_cleanup(tables->linker); + if (mfre) { + g_free(linker_data); + } + g_array_free(tables->rsdp, mfre); + g_array_free(tables->table_data, mfre); +} + +typedef +struct AcpiBuildState { + /* Copy of table in RAM (for patching). */ + uint8_t *table_ram; + uint32_t table_size; + /* Is table patched? */ + uint8_t patched; + PcGuestInfo *guest_info; +} AcpiBuildState; + +static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) +{ + Object *pci_host; + QObject *o; + bool ambiguous; + + pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous); + g_assert(!ambiguous); + g_assert(pci_host); + + o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_BASE, NULL); + if (!o) { + return false; + } + mcfg->mcfg_base = qint_get_int(qobject_to_qint(o)); + + o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL); + assert(o); + mcfg->mcfg_size = qint_get_int(qobject_to_qint(o)); + return true; +} + +static +void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) +{ + GArray *table_offsets; + unsigned facs, dsdt, rsdt; + AcpiCpuInfo cpu; + AcpiPmInfo pm; + AcpiMiscInfo misc; + AcpiMcfgInfo mcfg; + PcPciInfo pci; + uint8_t *u; + + acpi_get_cpu_info(&cpu); + acpi_get_pm_info(&pm); + acpi_get_dsdt(&misc); + acpi_get_hotplug_info(&misc); + acpi_get_misc_info(&misc); + acpi_get_pci_info(&pci); + + table_offsets = g_array_new(false, true /* clear */, + sizeof(uint32_t)); + ACPI_BUILD_DPRINTF(3, "init ACPI tables\n"); + + bios_linker_loader_alloc(tables->linker, ACPI_BUILD_TABLE_FILE, + 64 /* Ensure FACS is aligned */, + false /* high memory */); + + /* + * FACS is pointed to by FADT. + * We place it first since it's the only table that has alignment + * requirements. + */ + facs = tables->table_data->len; + build_facs(tables->table_data, tables->linker, guest_info); + + /* DSDT is pointed to by FADT */ + dsdt = tables->table_data->len; + build_dsdt(tables->table_data, tables->linker, &misc); + + /* ACPI tables pointed to by RSDT */ + acpi_add_table(table_offsets, tables->table_data); + build_fadt(tables->table_data, tables->linker, &pm, facs, dsdt); + acpi_add_table(table_offsets, tables->table_data); + + build_ssdt(tables->table_data, tables->linker, &cpu, &pm, &misc, &pci, + guest_info); + acpi_add_table(table_offsets, tables->table_data); + + build_madt(tables->table_data, tables->linker, &cpu, guest_info); + acpi_add_table(table_offsets, tables->table_data); + if (misc.has_hpet) { + build_hpet(tables->table_data, tables->linker); + } + if (guest_info->numa_nodes) { + acpi_add_table(table_offsets, tables->table_data); + build_srat(tables->table_data, tables->linker, &cpu, guest_info); + } + if (acpi_get_mcfg(&mcfg)) { + acpi_add_table(table_offsets, tables->table_data); + build_mcfg_q35(tables->table_data, tables->linker, &mcfg); + } + + /* Add tables supplied by user (if any) */ + for (u = acpi_table_first(); u; u = acpi_table_next(u)) { + unsigned len = acpi_table_len(u); + + acpi_add_table(table_offsets, tables->table_data); + g_array_append_vals(tables->table_data, u, len); + } + + /* RSDT is pointed to by RSDP */ + rsdt = tables->table_data->len; + build_rsdt(tables->table_data, tables->linker, table_offsets); + + /* RSDP is in FSEG memory, so allocate it separately */ + build_rsdp(tables->rsdp, tables->linker, rsdt); + + /* We'll expose it all to Guest so align size to reduce + * chance of size changes. + * RSDP is small so it's easy to keep it immutable, no need to + * bother with alignment. + */ + acpi_align_size(tables->table_data, 0x1000); + + acpi_align_size(tables->linker, 0x1000); + + /* Cleanup memory that's no longer used. */ + g_array_free(table_offsets, true); +} + +static void acpi_build_update(void *build_opaque, uint32_t offset) +{ + AcpiBuildState *build_state = build_opaque; + AcpiBuildTables tables; + + /* No state to update or already patched? Nothing to do. */ + if (!build_state || build_state->patched) { + return; + } + build_state->patched = 1; + + acpi_build_tables_init(&tables); + + acpi_build(build_state->guest_info, &tables); + + assert(acpi_data_len(tables.table_data) == build_state->table_size); + memcpy(build_state->table_ram, tables.table_data->data, + build_state->table_size); + + acpi_build_tables_cleanup(&tables, true); +} + +static void acpi_build_reset(void *build_opaque) +{ + AcpiBuildState *build_state = build_opaque; + build_state->patched = 0; +} + +static void *acpi_add_rom_blob(AcpiBuildState *build_state, GArray *blob, + const char *name) +{ + return rom_add_blob(name, blob->data, acpi_data_len(blob), -1, name, + acpi_build_update, build_state); +} + +static const VMStateDescription vmstate_acpi_build = { + .name = "acpi_build", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(patched, AcpiBuildState), + VMSTATE_END_OF_LIST() + }, +}; + +void acpi_setup(PcGuestInfo *guest_info) +{ + AcpiBuildTables tables; + AcpiBuildState *build_state; + + if (!guest_info->fw_cfg) { + ACPI_BUILD_DPRINTF(3, "No fw cfg. Bailing out.\n"); + return; + } + + if (!guest_info->has_acpi_build) { + ACPI_BUILD_DPRINTF(3, "ACPI build disabled. Bailing out.\n"); + return; + } + + if (!acpi_enabled) { + ACPI_BUILD_DPRINTF(3, "ACPI disabled. Bailing out.\n"); + return; + } + + build_state = g_malloc0(sizeof *build_state); + + build_state->guest_info = guest_info; + + acpi_build_tables_init(&tables); + acpi_build(build_state->guest_info, &tables); + + /* Now expose it all to Guest */ + build_state->table_ram = acpi_add_rom_blob(build_state, tables.table_data, + ACPI_BUILD_TABLE_FILE); + build_state->table_size = acpi_data_len(tables.table_data); + + acpi_add_rom_blob(NULL, tables.linker, "etc/table-loader"); + + /* + * RSDP is small so it's easy to keep it immutable, no need to + * bother with ROM blobs. + */ + fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_RSDP_FILE, + tables.rsdp->data, acpi_data_len(tables.rsdp)); + + qemu_register_reset(acpi_build_reset, build_state); + acpi_build_reset(build_state); + vmstate_register(NULL, 0, &vmstate_acpi_build, build_state); + + /* Cleanup tables but don't free the memory: we track it + * in build_state. + */ + acpi_build_tables_cleanup(&tables, false); +} diff --git a/hw/i386/acpi-build.h b/hw/i386/acpi-build.h new file mode 100644 index 0000000000..e57b1aafdc --- /dev/null +++ b/hw/i386/acpi-build.h @@ -0,0 +1,9 @@ + +#ifndef HW_I386_ACPI_BUILD_H +#define HW_I386_ACPI_BUILD_H + +#include "qemu/typedefs.h" + +void acpi_setup(PcGuestInfo *); + +#endif diff --git a/hw/i386/acpi-defs.h b/hw/i386/acpi-defs.h new file mode 100644 index 0000000000..78ca20489f --- /dev/null +++ b/hw/i386/acpi-defs.h @@ -0,0 +1,331 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef QEMU_ACPI_DEFS_H +#define QEMU_ACPI_DEFS_H + +enum { + ACPI_FADT_F_WBINVD, + ACPI_FADT_F_WBINVD_FLUSH, + ACPI_FADT_F_PROC_C1, + ACPI_FADT_F_P_LVL2_UP, + ACPI_FADT_F_PWR_BUTTON, + ACPI_FADT_F_SLP_BUTTON, + ACPI_FADT_F_FIX_RTC, + ACPI_FADT_F_RTC_S4, + ACPI_FADT_F_TMR_VAL_EXT, + ACPI_FADT_F_DCK_CAP, + ACPI_FADT_F_RESET_REG_SUP, + ACPI_FADT_F_SEALED_CASE, + ACPI_FADT_F_HEADLESS, + ACPI_FADT_F_CPU_SW_SLP, + ACPI_FADT_F_PCI_EXP_WAK, + ACPI_FADT_F_USE_PLATFORM_CLOCK, + ACPI_FADT_F_S4_RTC_STS_VALID, + ACPI_FADT_F_REMOTE_POWER_ON_CAPABLE, + ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL, + ACPI_FADT_F_FORCE_APIC_PHYSICAL_DESTINATION_MODE, + ACPI_FADT_F_HW_REDUCED_ACPI, + ACPI_FADT_F_LOW_POWER_S0_IDLE_CAPABLE, +}; + +/* + * ACPI 2.0 Generic Address Space definition. + */ +struct Acpi20GenericAddress { + uint8_t address_space_id; + uint8_t register_bit_width; + uint8_t register_bit_offset; + uint8_t reserved; + uint64_t address; +} QEMU_PACKED; +typedef struct Acpi20GenericAddress Acpi20GenericAddress; + +#define ACPI_RSDP_SIGNATURE 0x2052545020445352LL // "RSD PTR " + +struct AcpiRsdpDescriptor { /* Root System Descriptor Pointer */ + uint64_t signature; /* ACPI signature, contains "RSD PTR " */ + uint8_t checksum; /* To make sum of struct == 0 */ + uint8_t oem_id [6]; /* OEM identification */ + uint8_t revision; /* Must be 0 for 1.0, 2 for 2.0 */ + uint32_t rsdt_physical_address; /* 32-bit physical address of RSDT */ + uint32_t length; /* XSDT Length in bytes including hdr */ + uint64_t xsdt_physical_address; /* 64-bit physical address of XSDT */ + uint8_t extended_checksum; /* Checksum of entire table */ + uint8_t reserved [3]; /* Reserved field must be 0 */ +} QEMU_PACKED; +typedef struct AcpiRsdpDescriptor AcpiRsdpDescriptor; + +/* Table structure from Linux kernel (the ACPI tables are under the + BSD license) */ + + +#define ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \ + uint32_t signature; /* ACPI signature (4 ASCII characters) */ \ + uint32_t length; /* Length of table, in bytes, including header */ \ + uint8_t revision; /* ACPI Specification minor version # */ \ + uint8_t checksum; /* To make sum of entire table == 0 */ \ + uint8_t oem_id [6]; /* OEM identification */ \ + uint8_t oem_table_id [8]; /* OEM table identification */ \ + uint32_t oem_revision; /* OEM revision number */ \ + uint8_t asl_compiler_id [4]; /* ASL compiler vendor ID */ \ + uint32_t asl_compiler_revision; /* ASL compiler revision number */ + + +struct AcpiTableHeader /* ACPI common table header */ +{ + ACPI_TABLE_HEADER_DEF +} QEMU_PACKED; +typedef struct AcpiTableHeader AcpiTableHeader; + +/* + * ACPI 1.0 Fixed ACPI Description Table (FADT) + */ +#define ACPI_FACP_SIGNATURE 0x50434146 // FACP +struct AcpiFadtDescriptorRev1 +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t firmware_ctrl; /* Physical address of FACS */ + uint32_t dsdt; /* Physical address of DSDT */ + uint8_t model; /* System Interrupt Model */ + uint8_t reserved1; /* Reserved */ + uint16_t sci_int; /* System vector of SCI interrupt */ + uint32_t smi_cmd; /* Port address of SMI command port */ + uint8_t acpi_enable; /* Value to write to smi_cmd to enable ACPI */ + uint8_t acpi_disable; /* Value to write to smi_cmd to disable ACPI */ + uint8_t S4bios_req; /* Value to write to SMI CMD to enter S4BIOS state */ + uint8_t reserved2; /* Reserved - must be zero */ + uint32_t pm1a_evt_blk; /* Port address of Power Mgt 1a acpi_event Reg Blk */ + uint32_t pm1b_evt_blk; /* Port address of Power Mgt 1b acpi_event Reg Blk */ + uint32_t pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */ + uint32_t pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */ + uint32_t pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */ + uint32_t pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */ + uint32_t gpe0_blk; /* Port addr of General Purpose acpi_event 0 Reg Blk */ + uint32_t gpe1_blk; /* Port addr of General Purpose acpi_event 1 Reg Blk */ + uint8_t pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */ + uint8_t pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */ + uint8_t pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */ + uint8_t pm_tmr_len; /* Byte Length of ports at pm_tm_blk */ + uint8_t gpe0_blk_len; /* Byte Length of ports at gpe0_blk */ + uint8_t gpe1_blk_len; /* Byte Length of ports at gpe1_blk */ + uint8_t gpe1_base; /* Offset in gpe model where gpe1 events start */ + uint8_t reserved3; /* Reserved */ + uint16_t plvl2_lat; /* Worst case HW latency to enter/exit C2 state */ + uint16_t plvl3_lat; /* Worst case HW latency to enter/exit C3 state */ + uint16_t flush_size; /* Size of area read to flush caches */ + uint16_t flush_stride; /* Stride used in flushing caches */ + uint8_t duty_offset; /* Bit location of duty cycle field in p_cnt reg */ + uint8_t duty_width; /* Bit width of duty cycle field in p_cnt reg */ + uint8_t day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */ + uint8_t mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */ + uint8_t century; /* Index to century in RTC CMOS RAM */ + uint8_t reserved4; /* Reserved */ + uint8_t reserved4a; /* Reserved */ + uint8_t reserved4b; /* Reserved */ + uint32_t flags; +} QEMU_PACKED; +typedef struct AcpiFadtDescriptorRev1 AcpiFadtDescriptorRev1; + +/* + * ACPI 1.0 Root System Description Table (RSDT) + */ +#define ACPI_RSDT_SIGNATURE 0x54445352 // RSDT +struct AcpiRsdtDescriptorRev1 +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t table_offset_entry[0]; /* Array of pointers to other */ + /* ACPI tables */ +} QEMU_PACKED; +typedef struct AcpiRsdtDescriptorRev1 AcpiRsdtDescriptorRev1; + +/* + * ACPI 1.0 Firmware ACPI Control Structure (FACS) + */ +#define ACPI_FACS_SIGNATURE 0x53434146 // FACS +struct AcpiFacsDescriptorRev1 +{ + uint32_t signature; /* ACPI Signature */ + uint32_t length; /* Length of structure, in bytes */ + uint32_t hardware_signature; /* Hardware configuration signature */ + uint32_t firmware_waking_vector; /* ACPI OS waking vector */ + uint32_t global_lock; /* Global Lock */ + uint32_t flags; + uint8_t resverved3 [40]; /* Reserved - must be zero */ +} QEMU_PACKED; +typedef struct AcpiFacsDescriptorRev1 AcpiFacsDescriptorRev1; + +/* + * Differentiated System Description Table (DSDT) + */ +#define ACPI_DSDT_SIGNATURE 0x54445344 // DSDT + +/* + * MADT values and structures + */ + +/* Values for MADT PCATCompat */ + +#define ACPI_DUAL_PIC 0 +#define ACPI_MULTIPLE_APIC 1 + +/* Master MADT */ + +#define ACPI_APIC_SIGNATURE 0x43495041 // APIC +struct AcpiMultipleApicTable +{ + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t local_apic_address; /* Physical address of local APIC */ + uint32_t flags; +} QEMU_PACKED; +typedef struct AcpiMultipleApicTable AcpiMultipleApicTable; + +/* Values for Type in APIC sub-headers */ + +#define ACPI_APIC_PROCESSOR 0 +#define ACPI_APIC_IO 1 +#define ACPI_APIC_XRUPT_OVERRIDE 2 +#define ACPI_APIC_NMI 3 +#define ACPI_APIC_LOCAL_NMI 4 +#define ACPI_APIC_ADDRESS_OVERRIDE 5 +#define ACPI_APIC_IO_SAPIC 6 +#define ACPI_APIC_LOCAL_SAPIC 7 +#define ACPI_APIC_XRUPT_SOURCE 8 +#define ACPI_APIC_RESERVED 9 /* 9 and greater are reserved */ + +/* + * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE) + */ +#define ACPI_SUB_HEADER_DEF /* Common ACPI sub-structure header */\ + uint8_t type; \ + uint8_t length; + +/* Sub-structures for MADT */ + +struct AcpiMadtProcessorApic +{ + ACPI_SUB_HEADER_DEF + uint8_t processor_id; /* ACPI processor id */ + uint8_t local_apic_id; /* Processor's local APIC id */ + uint32_t flags; +} QEMU_PACKED; +typedef struct AcpiMadtProcessorApic AcpiMadtProcessorApic; + +struct AcpiMadtIoApic +{ + ACPI_SUB_HEADER_DEF + uint8_t io_apic_id; /* I/O APIC ID */ + uint8_t reserved; /* Reserved - must be zero */ + uint32_t address; /* APIC physical address */ + uint32_t interrupt; /* Global system interrupt where INTI + * lines start */ +} QEMU_PACKED; +typedef struct AcpiMadtIoApic AcpiMadtIoApic; + +struct AcpiMadtIntsrcovr { + ACPI_SUB_HEADER_DEF + uint8_t bus; + uint8_t source; + uint32_t gsi; + uint16_t flags; +} QEMU_PACKED; +typedef struct AcpiMadtIntsrcovr AcpiMadtIntsrcovr; + +struct AcpiMadtLocalNmi { + ACPI_SUB_HEADER_DEF + uint8_t processor_id; /* ACPI processor id */ + uint16_t flags; /* MPS INTI flags */ + uint8_t lint; /* Local APIC LINT# */ +} QEMU_PACKED; +typedef struct AcpiMadtLocalNmi AcpiMadtLocalNmi; + +/* + * HPET Description Table + */ +#define ACPI_HPET_SIGNATURE 0x54455048 // HPET +struct Acpi20Hpet { + ACPI_TABLE_HEADER_DEF /* ACPI common table header */ + uint32_t timer_block_id; + Acpi20GenericAddress addr; + uint8_t hpet_number; + uint16_t min_tick; + uint8_t page_protect; +} QEMU_PACKED; +typedef struct Acpi20Hpet Acpi20Hpet; + +/* + * SRAT (NUMA topology description) table + */ + +#define ACPI_SRAT_SIGNATURE 0x54415253 // SRAT +struct AcpiSystemResourceAffinityTable +{ + ACPI_TABLE_HEADER_DEF + uint32_t reserved1; + uint32_t reserved2[2]; +} QEMU_PACKED; +typedef struct AcpiSystemResourceAffinityTable AcpiSystemResourceAffinityTable; + +#define ACPI_SRAT_PROCESSOR 0 +#define ACPI_SRAT_MEMORY 1 + +struct AcpiSratProcessorAffinity +{ + ACPI_SUB_HEADER_DEF + uint8_t proximity_lo; + uint8_t local_apic_id; + uint32_t flags; + uint8_t local_sapic_eid; + uint8_t proximity_hi[3]; + uint32_t reserved; +} QEMU_PACKED; +typedef struct AcpiSratProcessorAffinity AcpiSratProcessorAffinity; + +struct AcpiSratMemoryAffinity +{ + ACPI_SUB_HEADER_DEF + uint8_t proximity[4]; + uint16_t reserved1; + uint64_t base_addr; + uint64_t range_length; + uint32_t reserved2; + uint32_t flags; + uint32_t reserved3[2]; +} QEMU_PACKED; +typedef struct AcpiSratMemoryAffinity AcpiSratMemoryAffinity; + +/* PCI fw r3.0 MCFG table. */ +/* Subtable */ +struct AcpiMcfgAllocation { + uint64_t address; /* Base address, processor-relative */ + uint16_t pci_segment; /* PCI segment group number */ + uint8_t start_bus_number; /* Starting PCI Bus number */ + uint8_t end_bus_number; /* Final PCI Bus number */ + uint32_t reserved; +} QEMU_PACKED; +typedef struct AcpiMcfgAllocation AcpiMcfgAllocation; + +#define ACPI_MCFG_SIGNATURE 0x4746434d // MCFG + +/* Reserved signature: ignored by OSPM */ +#define ACPI_RSRV_SIGNATURE 0x554d4551 // QEMU + +struct AcpiTableMcfg { + ACPI_TABLE_HEADER_DEF; + uint8_t reserved[8]; + AcpiMcfgAllocation allocation[0]; +} QEMU_PACKED; +typedef struct AcpiTableMcfg AcpiTableMcfg; + +#endif diff --git a/hw/i386/acpi-dsdt-cpu-hotplug.dsl b/hw/i386/acpi-dsdt-cpu-hotplug.dsl new file mode 100644 index 0000000000..c96ac42a31 --- /dev/null +++ b/hw/i386/acpi-dsdt-cpu-hotplug.dsl @@ -0,0 +1,93 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/**************************************************************** + * CPU hotplug + ****************************************************************/ + +Scope(\_SB) { + /* Objects filled in by run-time generated SSDT */ + External(NTFY, MethodObj) + External(CPON, PkgObj) + + /* Methods called by run-time generated SSDT Processor objects */ + Method(CPMA, 1, NotSerialized) { + // _MAT method - create an madt apic buffer + // Arg0 = Processor ID = Local APIC ID + // Local0 = CPON flag for this cpu + Store(DerefOf(Index(CPON, Arg0)), Local0) + // Local1 = Buffer (in madt apic form) to return + Store(Buffer(8) {0x00, 0x08, 0x00, 0x00, 0x00, 0, 0, 0}, Local1) + // Update the processor id, lapic id, and enable/disable status + Store(Arg0, Index(Local1, 2)) + Store(Arg0, Index(Local1, 3)) + Store(Local0, Index(Local1, 4)) + Return (Local1) + } + Method(CPST, 1, NotSerialized) { + // _STA method - return ON status of cpu + // Arg0 = Processor ID = Local APIC ID + // Local0 = CPON flag for this cpu + Store(DerefOf(Index(CPON, Arg0)), Local0) + If (Local0) { + Return (0xF) + } Else { + Return (0x0) + } + } + Method(CPEJ, 2, NotSerialized) { + // _EJ0 method - eject callback + Sleep(200) + } + + /* CPU hotplug notify method */ + OperationRegion(PRST, SystemIO, 0xaf00, 32) + Field(PRST, ByteAcc, NoLock, Preserve) { + PRS, 256 + } + Method(PRSC, 0) { + // Local5 = active cpu bitmap + Store(PRS, Local5) + // Local2 = last read byte from bitmap + Store(Zero, Local2) + // Local0 = Processor ID / APIC ID iterator + Store(Zero, Local0) + While (LLess(Local0, SizeOf(CPON))) { + // Local1 = CPON flag for this cpu + Store(DerefOf(Index(CPON, Local0)), Local1) + If (And(Local0, 0x07)) { + // Shift down previously read bitmap byte + ShiftRight(Local2, 1, Local2) + } Else { + // Read next byte from cpu bitmap + Store(DerefOf(Index(Local5, ShiftRight(Local0, 3))), Local2) + } + // Local3 = active state for this cpu + Store(And(Local2, 1), Local3) + + If (LNotEqual(Local1, Local3)) { + // State change - update CPON with new state + Store(Local3, Index(CPON, Local0)) + // Do CPU notify + If (LEqual(Local3, 1)) { + NTFY(Local0, 1) + } Else { + NTFY(Local0, 3) + } + } + Increment(Local0) + } + } +} diff --git a/hw/i386/acpi-dsdt-dbug.dsl b/hw/i386/acpi-dsdt-dbug.dsl new file mode 100644 index 0000000000..86230f75a0 --- /dev/null +++ b/hw/i386/acpi-dsdt-dbug.dsl @@ -0,0 +1,41 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/**************************************************************** + * Debugging + ****************************************************************/ + +Scope(\) { + /* Debug Output */ + OperationRegion(DBG, SystemIO, 0x0402, 0x01) + Field(DBG, ByteAcc, NoLock, Preserve) { + DBGB, 8, + } + + /* Debug method - use this method to send output to the QEMU + * BIOS debug port. This method handles strings, integers, + * and buffers. For example: DBUG("abc") DBUG(0x123) */ + Method(DBUG, 1) { + ToHexString(Arg0, Local0) + ToBuffer(Local0, Local0) + Subtract(SizeOf(Local0), 1, Local1) + Store(Zero, Local2) + While (LLess(Local2, Local1)) { + Store(DerefOf(Index(Local0, Local2)), DBGB) + Increment(Local2) + } + Store(0x0A, DBGB) + } +} diff --git a/hw/i386/acpi-dsdt-hpet.dsl b/hw/i386/acpi-dsdt-hpet.dsl new file mode 100644 index 0000000000..dfde174317 --- /dev/null +++ b/hw/i386/acpi-dsdt-hpet.dsl @@ -0,0 +1,51 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/**************************************************************** + * HPET + ****************************************************************/ + +Scope(\_SB) { + Device(HPET) { + Name(_HID, EISAID("PNP0103")) + Name(_UID, 0) + OperationRegion(HPTM, SystemMemory, 0xFED00000, 0x400) + Field(HPTM, DWordAcc, Lock, Preserve) { + VEND, 32, + PRD, 32, + } + Method(_STA, 0, NotSerialized) { + Store(VEND, Local0) + Store(PRD, Local1) + ShiftRight(Local0, 16, Local0) + If (LOr(LEqual(Local0, 0), LEqual(Local0, 0xffff))) { + Return (0x0) + } + If (LOr(LEqual(Local1, 0), LGreater(Local1, 100000000))) { + Return (0x0) + } + Return (0x0F) + } + Name(_CRS, ResourceTemplate() { +#if 0 /* This makes WinXP BSOD for not yet figured reasons. */ + IRQNoFlags() {2, 8} +#endif + Memory32Fixed(ReadOnly, + 0xFED00000, // Address Base + 0x00000400, // Address Length + ) + }) + } +} diff --git a/hw/i386/acpi-dsdt-isa.dsl b/hw/i386/acpi-dsdt-isa.dsl new file mode 100644 index 0000000000..89caa1649d --- /dev/null +++ b/hw/i386/acpi-dsdt-isa.dsl @@ -0,0 +1,117 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Common legacy ISA style devices. */ +Scope(\_SB.PCI0.ISA) { + + Device(RTC) { + Name(_HID, EisaId("PNP0B00")) + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x0070, 0x0070, 0x10, 0x02) + IRQNoFlags() { 8 } + IO(Decode16, 0x0072, 0x0072, 0x02, 0x06) + }) + } + + Device(KBD) { + Name(_HID, EisaId("PNP0303")) + Method(_STA, 0, NotSerialized) { + Return (0x0f) + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x0060, 0x0060, 0x01, 0x01) + IO(Decode16, 0x0064, 0x0064, 0x01, 0x01) + IRQNoFlags() { 1 } + }) + } + + Device(MOU) { + Name(_HID, EisaId("PNP0F13")) + Method(_STA, 0, NotSerialized) { + Return (0x0f) + } + Name(_CRS, ResourceTemplate() { + IRQNoFlags() { 12 } + }) + } + + Device(FDC0) { + Name(_HID, EisaId("PNP0700")) + Method(_STA, 0, NotSerialized) { + Store(FDEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x03F2, 0x03F2, 0x00, 0x04) + IO(Decode16, 0x03F7, 0x03F7, 0x00, 0x01) + IRQNoFlags() { 6 } + DMA(Compatibility, NotBusMaster, Transfer8) { 2 } + }) + } + + Device(LPT) { + Name(_HID, EisaId("PNP0400")) + Method(_STA, 0, NotSerialized) { + Store(LPEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x0378, 0x0378, 0x08, 0x08) + IRQNoFlags() { 7 } + }) + } + + Device(COM1) { + Name(_HID, EisaId("PNP0501")) + Name(_UID, 0x01) + Method(_STA, 0, NotSerialized) { + Store(CAEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x03F8, 0x03F8, 0x00, 0x08) + IRQNoFlags() { 4 } + }) + } + + Device(COM2) { + Name(_HID, EisaId("PNP0501")) + Name(_UID, 0x02) + Method(_STA, 0, NotSerialized) { + Store(CBEN, Local0) + If (LEqual(Local0, 0)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x02F8, 0x02F8, 0x00, 0x08) + IRQNoFlags() { 3 } + }) + } +} diff --git a/hw/i386/acpi-dsdt-pci-crs.dsl b/hw/i386/acpi-dsdt-pci-crs.dsl new file mode 100644 index 0000000000..b375a19cf6 --- /dev/null +++ b/hw/i386/acpi-dsdt-pci-crs.dsl @@ -0,0 +1,105 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* PCI CRS (current resources) definition. */ +Scope(\_SB.PCI0) { + + Name(CRES, ResourceTemplate() { + WordBusNumber(ResourceProducer, MinFixed, MaxFixed, PosDecode, + 0x0000, // Address Space Granularity + 0x0000, // Address Range Minimum + 0x00FF, // Address Range Maximum + 0x0000, // Address Translation Offset + 0x0100, // Address Length + ,, ) + IO(Decode16, + 0x0CF8, // Address Range Minimum + 0x0CF8, // Address Range Maximum + 0x01, // Address Alignment + 0x08, // Address Length + ) + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Address Space Granularity + 0x0000, // Address Range Minimum + 0x0CF7, // Address Range Maximum + 0x0000, // Address Translation Offset + 0x0CF8, // Address Length + ,, , TypeStatic) + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Address Space Granularity + 0x0D00, // Address Range Minimum + 0xFFFF, // Address Range Maximum + 0x0000, // Address Translation Offset + 0xF300, // Address Length + ,, , TypeStatic) + DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0x000A0000, // Address Range Minimum + 0x000BFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x00020000, // Address Length + ,, , AddressRangeMemory, TypeStatic) + DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0xE0000000, // Address Range Minimum + 0xFEBFFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x1EC00000, // Address Length + ,, PW32, AddressRangeMemory, TypeStatic) + }) + + Name(CR64, ResourceTemplate() { + QWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, + 0x00000000, // Address Space Granularity + 0x8000000000, // Address Range Minimum + 0xFFFFFFFFFF, // Address Range Maximum + 0x00000000, // Address Translation Offset + 0x8000000000, // Address Length + ,, PW64, AddressRangeMemory, TypeStatic) + }) + + Method(_CRS, 0) { + /* Fields provided by dynamically created ssdt */ + External(P0S, IntObj) + External(P0E, IntObj) + External(P1V, IntObj) + External(P1S, BuffObj) + External(P1E, BuffObj) + External(P1L, BuffObj) + + /* fixup 32bit pci io window */ + CreateDWordField(CRES, \_SB.PCI0.PW32._MIN, PS32) + CreateDWordField(CRES, \_SB.PCI0.PW32._MAX, PE32) + CreateDWordField(CRES, \_SB.PCI0.PW32._LEN, PL32) + Store(P0S, PS32) + Store(P0E, PE32) + Store(Add(Subtract(P0E, P0S), 1), PL32) + + If (LEqual(P1V, Zero)) { + Return (CRES) + } + + /* fixup 64bit pci io window */ + CreateQWordField(CR64, \_SB.PCI0.PW64._MIN, PS64) + CreateQWordField(CR64, \_SB.PCI0.PW64._MAX, PE64) + CreateQWordField(CR64, \_SB.PCI0.PW64._LEN, PL64) + Store(P1S, PS64) + Store(P1E, PE64) + Store(P1L, PL64) + /* add window and return result */ + ConcatenateResTemplate(CRES, CR64, Local0) + Return (Local0) + } +} diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl new file mode 100644 index 0000000000..90efce0d18 --- /dev/null +++ b/hw/i386/acpi-dsdt.dsl @@ -0,0 +1,343 @@ +/* + * Bochs/QEMU ACPI DSDT ASL definition + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +ACPI_EXTRACT_ALL_CODE AcpiDsdtAmlCode + +DefinitionBlock ( + "acpi-dsdt.aml", // Output Filename + "DSDT", // Signature + 0x01, // DSDT Compliance Revision + "BXPC", // OEMID + "BXDSDT", // TABLE ID + 0x1 // OEM Revision + ) +{ + +#include "acpi-dsdt-dbug.dsl" + + +/**************************************************************** + * PCI Bus definition + ****************************************************************/ + + Scope(\_SB) { + Device(PCI0) { + Name(_HID, EisaId("PNP0A03")) + Name(_ADR, 0x00) + Name(_UID, 1) + } + } + +#include "acpi-dsdt-pci-crs.dsl" +#include "acpi-dsdt-hpet.dsl" + + +/**************************************************************** + * VGA + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(VGA) { + Name(_ADR, 0x00020000) + OperationRegion(PCIC, PCI_Config, Zero, 0x4) + Field(PCIC, DWordAcc, NoLock, Preserve) { + VEND, 32 + } + Method(_S1D, 0, NotSerialized) { + Return (0x00) + } + Method(_S2D, 0, NotSerialized) { + Return (0x00) + } + Method(_S3D, 0, NotSerialized) { + If (LEqual(VEND, 0x1001b36)) { + Return (0x03) // QXL + } Else { + Return (0x00) + } + } + } + } + + +/**************************************************************** + * PIIX4 PM + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(PX13) { + Name(_ADR, 0x00010003) + OperationRegion(P13C, PCI_Config, 0x00, 0xff) + } + } + + +/**************************************************************** + * PIIX3 ISA bridge + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(ISA) { + Name(_ADR, 0x00010000) + + /* PIIX PCI to ISA irq remapping */ + OperationRegion(P40C, PCI_Config, 0x60, 0x04) + + /* enable bits */ + Field(\_SB.PCI0.PX13.P13C, AnyAcc, NoLock, Preserve) { + Offset(0x5f), + , 7, + LPEN, 1, // LPT + Offset(0x67), + , 3, + CAEN, 1, // COM1 + , 3, + CBEN, 1, // COM2 + } + Name(FDEN, 1) + } + } + +#include "acpi-dsdt-isa.dsl" + + +/**************************************************************** + * PCI hotplug + ****************************************************************/ + + Scope(\_SB.PCI0) { + OperationRegion(PCST, SystemIO, 0xae00, 0x08) + Field(PCST, DWordAcc, NoLock, WriteAsZeros) { + PCIU, 32, + PCID, 32, + } + + OperationRegion(SEJ, SystemIO, 0xae08, 0x04) + Field(SEJ, DWordAcc, NoLock, WriteAsZeros) { + B0EJ, 32, + } + + /* Methods called by bulk generated PCI devices below */ + + /* Methods called by hotplug devices */ + Method(PCEJ, 1, NotSerialized) { + // _EJ0 method - eject callback + Store(ShiftLeft(1, Arg0), B0EJ) + Return (0x0) + } + + /* Hotplug notification method supplied by SSDT */ + External(\_SB.PCI0.PCNT, MethodObj) + + /* PCI hotplug notify method */ + Method(PCNF, 0) { + // Local0 = iterator + Store(Zero, Local0) + While (LLess(Local0, 31)) { + Increment(Local0) + If (And(PCIU, ShiftLeft(1, Local0))) { + PCNT(Local0, 1) + } + If (And(PCID, ShiftLeft(1, Local0))) { + PCNT(Local0, 3) + } + } + } + } + + +/**************************************************************** + * PCI IRQs + ****************************************************************/ + + Scope(\_SB) { + Scope(PCI0) { + Name(_PRT, Package() { + /* PCI IRQ routing table, example from ACPI 2.0a specification, + section 6.2.8.1 */ + /* Note: we provide the same info as the PCI routing + table of the Bochs BIOS */ + +#define prt_slot(nr, lnk0, lnk1, lnk2, lnk3) \ + Package() { nr##ffff, 0, lnk0, 0 }, \ + Package() { nr##ffff, 1, lnk1, 0 }, \ + Package() { nr##ffff, 2, lnk2, 0 }, \ + Package() { nr##ffff, 3, lnk3, 0 } + +#define prt_slot0(nr) prt_slot(nr, LNKD, LNKA, LNKB, LNKC) +#define prt_slot1(nr) prt_slot(nr, LNKA, LNKB, LNKC, LNKD) +#define prt_slot2(nr) prt_slot(nr, LNKB, LNKC, LNKD, LNKA) +#define prt_slot3(nr) prt_slot(nr, LNKC, LNKD, LNKA, LNKB) + + prt_slot0(0x0000), + /* Device 1 is power mgmt device, and can only use irq 9 */ + prt_slot(0x0001, LNKS, LNKB, LNKC, LNKD), + prt_slot2(0x0002), + prt_slot3(0x0003), + prt_slot0(0x0004), + prt_slot1(0x0005), + prt_slot2(0x0006), + prt_slot3(0x0007), + prt_slot0(0x0008), + prt_slot1(0x0009), + prt_slot2(0x000a), + prt_slot3(0x000b), + prt_slot0(0x000c), + prt_slot1(0x000d), + prt_slot2(0x000e), + prt_slot3(0x000f), + prt_slot0(0x0010), + prt_slot1(0x0011), + prt_slot2(0x0012), + prt_slot3(0x0013), + prt_slot0(0x0014), + prt_slot1(0x0015), + prt_slot2(0x0016), + prt_slot3(0x0017), + prt_slot0(0x0018), + prt_slot1(0x0019), + prt_slot2(0x001a), + prt_slot3(0x001b), + prt_slot0(0x001c), + prt_slot1(0x001d), + prt_slot2(0x001e), + prt_slot3(0x001f), + }) + } + + Field(PCI0.ISA.P40C, ByteAcc, NoLock, Preserve) { + PRQ0, 8, + PRQ1, 8, + PRQ2, 8, + PRQ3, 8 + } + + Method(IQST, 1, NotSerialized) { + // _STA method - get status + If (And(0x80, Arg0)) { + Return (0x09) + } + Return (0x0B) + } + Method(IQCR, 1, NotSerialized) { + // _CRS method - get current settings + Name(PRR0, ResourceTemplate() { + Interrupt(, Level, ActiveHigh, Shared) { 0 } + }) + CreateDWordField(PRR0, 0x05, PRRI) + If (LLess(Arg0, 0x80)) { + Store(Arg0, PRRI) + } + Return (PRR0) + } + +#define define_link(link, uid, reg) \ + Device(link) { \ + Name(_HID, EISAID("PNP0C0F")) \ + Name(_UID, uid) \ + Name(_PRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + 5, 10, 11 \ + } \ + }) \ + Method(_STA, 0, NotSerialized) { \ + Return (IQST(reg)) \ + } \ + Method(_DIS, 0, NotSerialized) { \ + Or(reg, 0x80, reg) \ + } \ + Method(_CRS, 0, NotSerialized) { \ + Return (IQCR(reg)) \ + } \ + Method(_SRS, 1, NotSerialized) { \ + CreateDWordField(Arg0, 0x05, PRRI) \ + Store(PRRI, reg) \ + } \ + } + + define_link(LNKA, 0, PRQ0) + define_link(LNKB, 1, PRQ1) + define_link(LNKC, 2, PRQ2) + define_link(LNKD, 3, PRQ3) + + Device(LNKS) { + Name(_HID, EISAID("PNP0C0F")) + Name(_UID, 4) + Name(_PRS, ResourceTemplate() { + Interrupt(, Level, ActiveHigh, Shared) { 9 } + }) + + // The SCI cannot be disabled and is always attached to GSI 9, + // so these are no-ops. We only need this link to override the + // polarity to active high and match the content of the MADT. + Method(_STA, 0, NotSerialized) { Return (0x0b) } + Method(_DIS, 0, NotSerialized) { } + Method(_CRS, 0, NotSerialized) { Return (_PRS) } + Method(_SRS, 1, NotSerialized) { } + } + } + +#include "acpi-dsdt-cpu-hotplug.dsl" + + +/**************************************************************** + * General purpose events + ****************************************************************/ + + Scope(\_GPE) { + Name(_HID, "ACPI0006") + + Method(_L00) { + } + Method(_E01) { + // PCI hotplug event + \_SB.PCI0.PCNF() + } + Method(_E02) { + // CPU hotplug event + \_SB.PRSC() + } + Method(_L03) { + } + Method(_L04) { + } + Method(_L05) { + } + Method(_L06) { + } + Method(_L07) { + } + Method(_L08) { + } + Method(_L09) { + } + Method(_L0A) { + } + Method(_L0B) { + } + Method(_L0C) { + } + Method(_L0D) { + } + Method(_L0E) { + } + Method(_L0F) { + } + } +} diff --git a/hw/i386/acpi-dsdt.hex.generated b/hw/i386/acpi-dsdt.hex.generated new file mode 100644 index 0000000000..2c011070c4 --- /dev/null +++ b/hw/i386/acpi-dsdt.hex.generated @@ -0,0 +1,4409 @@ +static unsigned char AcpiDsdtAmlCode[] = { +0x44, +0x53, +0x44, +0x54, +0x37, +0x11, +0x0, +0x0, +0x1, +0xe0, +0x42, +0x58, +0x50, +0x43, +0x0, +0x0, +0x42, +0x58, +0x44, +0x53, +0x44, +0x54, +0x0, +0x0, +0x1, +0x0, +0x0, +0x0, +0x49, +0x4e, +0x54, +0x4c, +0x23, +0x8, +0x13, +0x20, +0x10, +0x49, +0x4, +0x5c, +0x0, +0x5b, +0x80, +0x44, +0x42, +0x47, +0x5f, +0x1, +0xb, +0x2, +0x4, +0x1, +0x5b, +0x81, +0xb, +0x44, +0x42, +0x47, +0x5f, +0x1, +0x44, +0x42, +0x47, +0x42, +0x8, +0x14, +0x2c, +0x44, +0x42, +0x55, +0x47, +0x1, +0x98, +0x68, +0x60, +0x96, +0x60, +0x60, +0x74, +0x87, +0x60, +0x1, +0x61, +0x70, +0x0, +0x62, +0xa2, +0x10, +0x95, +0x62, +0x61, +0x70, +0x83, +0x88, +0x60, +0x62, +0x0, +0x44, +0x42, +0x47, +0x42, +0x75, +0x62, +0x70, +0xa, +0xa, +0x44, +0x42, +0x47, +0x42, +0x10, +0x22, +0x5f, +0x53, +0x42, +0x5f, +0x5b, +0x82, +0x1b, +0x50, +0x43, +0x49, +0x30, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xa, +0x3, +0x8, +0x5f, +0x41, +0x44, +0x52, +0x0, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x1, +0x10, +0x4e, +0x15, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x8, +0x43, +0x52, +0x45, +0x53, +0x11, +0x42, +0x7, +0xa, +0x6e, +0x88, +0xd, +0x0, +0x2, +0xc, +0x0, +0x0, +0x0, +0x0, +0x0, +0xff, +0x0, +0x0, +0x0, +0x0, +0x1, +0x47, +0x1, +0xf8, +0xc, +0xf8, +0xc, +0x1, +0x8, +0x88, +0xd, +0x0, +0x1, +0xc, +0x3, +0x0, +0x0, +0x0, +0x0, +0xf7, +0xc, +0x0, +0x0, +0xf8, +0xc, +0x88, +0xd, +0x0, +0x1, +0xc, +0x3, +0x0, +0x0, +0x0, +0xd, +0xff, +0xff, +0x0, +0x0, +0x0, +0xf3, +0x87, +0x17, +0x0, +0x0, +0xc, +0x3, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0xa, +0x0, +0xff, +0xff, +0xb, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x2, +0x0, +0x87, +0x17, +0x0, +0x0, +0xc, +0x1, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0xe0, +0xff, +0xff, +0xbf, +0xfe, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0xc0, +0x1e, +0x79, +0x0, +0x8, +0x43, +0x52, +0x36, +0x34, +0x11, +0x33, +0xa, +0x30, +0x8a, +0x2b, +0x0, +0x0, +0xc, +0x3, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x80, +0x0, +0x0, +0x0, +0xff, +0xff, +0xff, +0xff, +0xff, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x80, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x41, +0xa, +0x5f, +0x43, +0x52, +0x53, +0x0, +0x8a, +0x43, +0x52, +0x45, +0x53, +0xa, +0x5c, +0x50, +0x53, +0x33, +0x32, +0x8a, +0x43, +0x52, +0x45, +0x53, +0xa, +0x60, +0x50, +0x45, +0x33, +0x32, +0x8a, +0x43, +0x52, +0x45, +0x53, +0xa, +0x68, +0x50, +0x4c, +0x33, +0x32, +0x70, +0x50, +0x30, +0x53, +0x5f, +0x50, +0x53, +0x33, +0x32, +0x70, +0x50, +0x30, +0x45, +0x5f, +0x50, +0x45, +0x33, +0x32, +0x70, +0x72, +0x74, +0x50, +0x30, +0x45, +0x5f, +0x50, +0x30, +0x53, +0x5f, +0x0, +0x1, +0x0, +0x50, +0x4c, +0x33, +0x32, +0xa0, +0xc, +0x93, +0x50, +0x31, +0x56, +0x5f, +0x0, +0xa4, +0x43, +0x52, +0x45, +0x53, +0x8f, +0x43, +0x52, +0x36, +0x34, +0xa, +0xe, +0x50, +0x53, +0x36, +0x34, +0x8f, +0x43, +0x52, +0x36, +0x34, +0xa, +0x16, +0x50, +0x45, +0x36, +0x34, +0x8f, +0x43, +0x52, +0x36, +0x34, +0xa, +0x26, +0x50, +0x4c, +0x36, +0x34, +0x70, +0x50, +0x31, +0x53, +0x5f, +0x50, +0x53, +0x36, +0x34, +0x70, +0x50, +0x31, +0x45, +0x5f, +0x50, +0x45, +0x36, +0x34, +0x70, +0x50, +0x31, +0x4c, +0x5f, +0x50, +0x4c, +0x36, +0x34, +0x84, +0x43, +0x52, +0x45, +0x53, +0x43, +0x52, +0x36, +0x34, +0x60, +0xa4, +0x60, +0x10, +0x4d, +0x8, +0x5f, +0x53, +0x42, +0x5f, +0x5b, +0x82, +0x45, +0x8, +0x48, +0x50, +0x45, +0x54, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x1, +0x3, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x5b, +0x80, +0x48, +0x50, +0x54, +0x4d, +0x0, +0xc, +0x0, +0x0, +0xd0, +0xfe, +0xb, +0x0, +0x4, +0x5b, +0x81, +0x10, +0x48, +0x50, +0x54, +0x4d, +0x13, +0x56, +0x45, +0x4e, +0x44, +0x20, +0x50, +0x52, +0x44, +0x5f, +0x20, +0x14, +0x36, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x56, +0x45, +0x4e, +0x44, +0x60, +0x70, +0x50, +0x52, +0x44, +0x5f, +0x61, +0x7a, +0x60, +0xa, +0x10, +0x60, +0xa0, +0xc, +0x91, +0x93, +0x60, +0x0, +0x93, +0x60, +0xb, +0xff, +0xff, +0xa4, +0x0, +0xa0, +0xe, +0x91, +0x93, +0x61, +0x0, +0x94, +0x61, +0xc, +0x0, +0xe1, +0xf5, +0x5, +0xa4, +0x0, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x11, +0xa, +0xe, +0x86, +0x9, +0x0, +0x0, +0x0, +0x0, +0xd0, +0xfe, +0x0, +0x4, +0x0, +0x0, +0x79, +0x0, +0x10, +0x40, +0x6, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x5b, +0x82, +0x43, +0x5, +0x56, +0x47, +0x41, +0x5f, +0x8, +0x5f, +0x41, +0x44, +0x52, +0xc, +0x0, +0x0, +0x2, +0x0, +0x5b, +0x80, +0x50, +0x43, +0x49, +0x43, +0x2, +0x0, +0xa, +0x4, +0x5b, +0x81, +0xb, +0x50, +0x43, +0x49, +0x43, +0x3, +0x56, +0x45, +0x4e, +0x44, +0x20, +0x14, +0x8, +0x5f, +0x53, +0x31, +0x44, +0x0, +0xa4, +0x0, +0x14, +0x8, +0x5f, +0x53, +0x32, +0x44, +0x0, +0xa4, +0x0, +0x14, +0x19, +0x5f, +0x53, +0x33, +0x44, +0x0, +0xa0, +0xe, +0x93, +0x56, +0x45, +0x4e, +0x44, +0xc, +0x36, +0x1b, +0x0, +0x1, +0xa4, +0xa, +0x3, +0xa1, +0x3, +0xa4, +0x0, +0x10, +0x25, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x5b, +0x82, +0x19, +0x50, +0x58, +0x31, +0x33, +0x8, +0x5f, +0x41, +0x44, +0x52, +0xc, +0x3, +0x0, +0x1, +0x0, +0x5b, +0x80, +0x50, +0x31, +0x33, +0x43, +0x2, +0x0, +0xa, +0xff, +0x10, +0x46, +0x5, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x5b, +0x82, +0x49, +0x4, +0x49, +0x53, +0x41, +0x5f, +0x8, +0x5f, +0x41, +0x44, +0x52, +0xc, +0x0, +0x0, +0x1, +0x0, +0x5b, +0x80, +0x50, +0x34, +0x30, +0x43, +0x2, +0xa, +0x60, +0xa, +0x4, +0x5b, +0x81, +0x26, +0x5e, +0x2e, +0x50, +0x58, +0x31, +0x33, +0x50, +0x31, +0x33, +0x43, +0x0, +0x0, +0x48, +0x2f, +0x0, +0x7, +0x4c, +0x50, +0x45, +0x4e, +0x1, +0x0, +0x38, +0x0, +0x3, +0x43, +0x41, +0x45, +0x4e, +0x1, +0x0, +0x3, +0x43, +0x42, +0x45, +0x4e, +0x1, +0x8, +0x46, +0x44, +0x45, +0x4e, +0x1, +0x10, +0x4c, +0x1b, +0x2f, +0x3, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x49, +0x53, +0x41, +0x5f, +0x5b, +0x82, +0x2d, +0x52, +0x54, +0x43, +0x5f, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xb, +0x0, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x18, +0xa, +0x15, +0x47, +0x1, +0x70, +0x0, +0x70, +0x0, +0x10, +0x2, +0x22, +0x0, +0x1, +0x47, +0x1, +0x72, +0x0, +0x72, +0x0, +0x2, +0x6, +0x79, +0x0, +0x5b, +0x82, +0x37, +0x4b, +0x42, +0x44, +0x5f, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x3, +0x3, +0x14, +0x9, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x18, +0xa, +0x15, +0x47, +0x1, +0x60, +0x0, +0x60, +0x0, +0x1, +0x1, +0x47, +0x1, +0x64, +0x0, +0x64, +0x0, +0x1, +0x1, +0x22, +0x2, +0x0, +0x79, +0x0, +0x5b, +0x82, +0x27, +0x4d, +0x4f, +0x55, +0x5f, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xf, +0x13, +0x14, +0x9, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x8, +0xa, +0x5, +0x22, +0x0, +0x10, +0x79, +0x0, +0x5b, +0x82, +0x4a, +0x4, +0x46, +0x44, +0x43, +0x30, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x7, +0x0, +0x14, +0x18, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x46, +0x44, +0x45, +0x4e, +0x60, +0xa0, +0x6, +0x93, +0x60, +0x0, +0xa4, +0x0, +0xa1, +0x4, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x1b, +0xa, +0x18, +0x47, +0x1, +0xf2, +0x3, +0xf2, +0x3, +0x0, +0x4, +0x47, +0x1, +0xf7, +0x3, +0xf7, +0x3, +0x0, +0x1, +0x22, +0x40, +0x0, +0x2a, +0x4, +0x0, +0x79, +0x0, +0x5b, +0x82, +0x3e, +0x4c, +0x50, +0x54, +0x5f, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x4, +0x0, +0x14, +0x18, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x4c, +0x50, +0x45, +0x4e, +0x60, +0xa0, +0x6, +0x93, +0x60, +0x0, +0xa4, +0x0, +0xa1, +0x4, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x10, +0xa, +0xd, +0x47, +0x1, +0x78, +0x3, +0x78, +0x3, +0x8, +0x8, +0x22, +0x80, +0x0, +0x79, +0x0, +0x5b, +0x82, +0x45, +0x4, +0x43, +0x4f, +0x4d, +0x31, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x5, +0x1, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x1, +0x14, +0x18, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x43, +0x41, +0x45, +0x4e, +0x60, +0xa0, +0x6, +0x93, +0x60, +0x0, +0xa4, +0x0, +0xa1, +0x4, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x10, +0xa, +0xd, +0x47, +0x1, +0xf8, +0x3, +0xf8, +0x3, +0x0, +0x8, +0x22, +0x10, +0x0, +0x79, +0x0, +0x5b, +0x82, +0x46, +0x4, +0x43, +0x4f, +0x4d, +0x32, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x5, +0x1, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x2, +0x14, +0x18, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x43, +0x42, +0x45, +0x4e, +0x60, +0xa0, +0x6, +0x93, +0x60, +0x0, +0xa4, +0x0, +0xa1, +0x4, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x10, +0xa, +0xd, +0x47, +0x1, +0xf8, +0x2, +0xf8, +0x2, +0x0, +0x8, +0x22, +0x8, +0x0, +0x79, +0x0, +0x10, +0x4b, +0x8, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x5b, +0x80, +0x50, +0x43, +0x53, +0x54, +0x1, +0xb, +0x0, +0xae, +0xa, +0x8, +0x5b, +0x81, +0x10, +0x50, +0x43, +0x53, +0x54, +0x43, +0x50, +0x43, +0x49, +0x55, +0x20, +0x50, +0x43, +0x49, +0x44, +0x20, +0x5b, +0x80, +0x53, +0x45, +0x4a, +0x5f, +0x1, +0xb, +0x8, +0xae, +0xa, +0x4, +0x5b, +0x81, +0xb, +0x53, +0x45, +0x4a, +0x5f, +0x43, +0x42, +0x30, +0x45, +0x4a, +0x20, +0x14, +0x11, +0x50, +0x43, +0x45, +0x4a, +0x1, +0x70, +0x79, +0x1, +0x68, +0x0, +0x42, +0x30, +0x45, +0x4a, +0xa4, +0x0, +0x14, +0x36, +0x50, +0x43, +0x4e, +0x46, +0x0, +0x70, +0x0, +0x60, +0xa2, +0x2c, +0x95, +0x60, +0xa, +0x1f, +0x75, +0x60, +0xa0, +0x11, +0x7b, +0x50, +0x43, +0x49, +0x55, +0x79, +0x1, +0x60, +0x0, +0x0, +0x50, +0x43, +0x4e, +0x54, +0x60, +0x1, +0xa0, +0x12, +0x7b, +0x50, +0x43, +0x49, +0x44, +0x79, +0x1, +0x60, +0x0, +0x0, +0x50, +0x43, +0x4e, +0x54, +0x60, +0xa, +0x3, +0x10, +0x4a, +0xa0, +0x5f, +0x53, +0x42, +0x5f, +0x10, +0x47, +0x74, +0x50, +0x43, +0x49, +0x30, +0x8, +0x5f, +0x50, +0x52, +0x54, +0x12, +0x4b, +0x73, +0x80, +0x12, +0xb, +0x4, +0xb, +0xff, +0xff, +0x0, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xb, +0x4, +0xb, +0xff, +0xff, +0x1, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xc, +0x4, +0xb, +0xff, +0xff, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xc, +0x4, +0xb, +0xff, +0xff, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x53, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x5b, +0x81, +0x24, +0x2f, +0x3, +0x50, +0x43, +0x49, +0x30, +0x49, +0x53, +0x41, +0x5f, +0x50, +0x34, +0x30, +0x43, +0x1, +0x50, +0x52, +0x51, +0x30, +0x8, +0x50, +0x52, +0x51, +0x31, +0x8, +0x50, +0x52, +0x51, +0x32, +0x8, +0x50, +0x52, +0x51, +0x33, +0x8, +0x14, +0x13, +0x49, +0x51, +0x53, +0x54, +0x1, +0xa0, +0x9, +0x7b, +0xa, +0x80, +0x68, +0x0, +0xa4, +0xa, +0x9, +0xa4, +0xa, +0xb, +0x14, +0x36, +0x49, +0x51, +0x43, +0x52, +0x1, +0x8, +0x50, +0x52, +0x52, +0x30, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x0, +0x0, +0x0, +0x0, +0x79, +0x0, +0x8a, +0x50, +0x52, +0x52, +0x30, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0xa0, +0xb, +0x95, +0x68, +0xa, +0x80, +0x70, +0x68, +0x50, +0x52, +0x52, +0x49, +0xa4, +0x50, +0x52, +0x52, +0x30, +0x5b, +0x82, +0x4c, +0x7, +0x4c, +0x4e, +0x4b, +0x41, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x30, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x30, +0xa, +0x80, +0x50, +0x52, +0x51, +0x30, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x30, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x30, +0x5b, +0x82, +0x4c, +0x7, +0x4c, +0x4e, +0x4b, +0x42, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x1, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x31, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x31, +0xa, +0x80, +0x50, +0x52, +0x51, +0x31, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x31, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x31, +0x5b, +0x82, +0x4d, +0x7, +0x4c, +0x4e, +0x4b, +0x43, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x2, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x32, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x32, +0xa, +0x80, +0x50, +0x52, +0x51, +0x32, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x32, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x32, +0x5b, +0x82, +0x4d, +0x7, +0x4c, +0x4e, +0x4b, +0x44, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x3, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x33, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x33, +0xa, +0x80, +0x50, +0x52, +0x51, +0x33, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x33, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x33, +0x5b, +0x82, +0x4f, +0x4, +0x4c, +0x4e, +0x4b, +0x53, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x4, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x9, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x9, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0xa, +0xb, +0x14, +0x6, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x14, +0xb, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x5f, +0x50, +0x52, +0x53, +0x14, +0x6, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x10, +0x47, +0xe, +0x5f, +0x53, +0x42, +0x5f, +0x14, +0x35, +0x43, +0x50, +0x4d, +0x41, +0x1, +0x70, +0x83, +0x88, +0x43, +0x50, +0x4f, +0x4e, +0x68, +0x0, +0x60, +0x70, +0x11, +0xb, +0xa, +0x8, +0x0, +0x8, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x61, +0x70, +0x68, +0x88, +0x61, +0xa, +0x2, +0x0, +0x70, +0x68, +0x88, +0x61, +0xa, +0x3, +0x0, +0x70, +0x60, +0x88, +0x61, +0xa, +0x4, +0x0, +0xa4, +0x61, +0x14, +0x1a, +0x43, +0x50, +0x53, +0x54, +0x1, +0x70, +0x83, +0x88, +0x43, +0x50, +0x4f, +0x4e, +0x68, +0x0, +0x60, +0xa0, +0x5, +0x60, +0xa4, +0xa, +0xf, +0xa1, +0x3, +0xa4, +0x0, +0x14, +0xa, +0x43, +0x50, +0x45, +0x4a, +0x2, +0x5b, +0x22, +0xa, +0xc8, +0x5b, +0x80, +0x50, +0x52, +0x53, +0x54, +0x1, +0xb, +0x0, +0xaf, +0xa, +0x20, +0x5b, +0x81, +0xc, +0x50, +0x52, +0x53, +0x54, +0x1, +0x50, +0x52, +0x53, +0x5f, +0x40, +0x10, +0x14, +0x4a, +0x6, +0x50, +0x52, +0x53, +0x43, +0x0, +0x70, +0x50, +0x52, +0x53, +0x5f, +0x65, +0x70, +0x0, +0x62, +0x70, +0x0, +0x60, +0xa2, +0x46, +0x5, +0x95, +0x60, +0x87, +0x43, +0x50, +0x4f, +0x4e, +0x70, +0x83, +0x88, +0x43, +0x50, +0x4f, +0x4e, +0x60, +0x0, +0x61, +0xa0, +0xa, +0x7b, +0x60, +0xa, +0x7, +0x0, +0x7a, +0x62, +0x1, +0x62, +0xa1, +0xc, +0x70, +0x83, +0x88, +0x65, +0x7a, +0x60, +0xa, +0x3, +0x0, +0x0, +0x62, +0x70, +0x7b, +0x62, +0x1, +0x0, +0x63, +0xa0, +0x22, +0x92, +0x93, +0x61, +0x63, +0x70, +0x63, +0x88, +0x43, +0x50, +0x4f, +0x4e, +0x60, +0x0, +0xa0, +0xa, +0x93, +0x63, +0x1, +0x4e, +0x54, +0x46, +0x59, +0x60, +0x1, +0xa1, +0x8, +0x4e, +0x54, +0x46, +0x59, +0x60, +0xa, +0x3, +0x75, +0x60, +0x10, +0x4e, +0x9, +0x5f, +0x47, +0x50, +0x45, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xd, +0x41, +0x43, +0x50, +0x49, +0x30, +0x30, +0x30, +0x36, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x30, +0x0, +0x14, +0x15, +0x5f, +0x45, +0x30, +0x31, +0x0, +0x5c, +0x2f, +0x3, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x50, +0x43, +0x4e, +0x46, +0x14, +0x10, +0x5f, +0x45, +0x30, +0x32, +0x0, +0x5c, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x52, +0x53, +0x43, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x33, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x34, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x35, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x36, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x37, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x38, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x39, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x41, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x42, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x43, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x44, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x45, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x46, +0x0 +}; diff --git a/hw/i386/bios-linker-loader.c b/hw/i386/bios-linker-loader.c new file mode 100644 index 0000000000..083385332e --- /dev/null +++ b/hw/i386/bios-linker-loader.c @@ -0,0 +1,158 @@ +/* Dynamic linker/loader of ACPI tables + * + * Copyright (C) 2013 Red Hat Inc + * + * Author: Michael S. Tsirkin <mst@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "bios-linker-loader.h" +#include "hw/nvram/fw_cfg.h" + +#include <string.h> +#include <assert.h> +#include "qemu/bswap.h" + +#define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH + +struct BiosLinkerLoaderEntry { + uint32_t command; + union { + /* + * COMMAND_ALLOCATE - allocate a table from @alloc.file + * subject to @alloc.align alignment (must be power of 2) + * and @alloc.zone (can be HIGH or FSEG) requirements. + * + * Must appear exactly once for each file, and before + * this file is referenced by any other command. + */ + struct { + char file[BIOS_LINKER_LOADER_FILESZ]; + uint32_t align; + uint8_t zone; + } alloc; + + /* + * COMMAND_ADD_POINTER - patch the table (originating from + * @dest_file) at @pointer.offset, by adding a pointer to the table + * originating from @src_file. 1,2,4 or 8 byte unsigned + * addition is used depending on @pointer.size. + */ + struct { + char dest_file[BIOS_LINKER_LOADER_FILESZ]; + char src_file[BIOS_LINKER_LOADER_FILESZ]; + uint32_t offset; + uint8_t size; + } pointer; + + /* + * COMMAND_ADD_CHECKSUM - calculate checksum of the range specified by + * @cksum_start and @cksum_length fields, + * and then add the value at @cksum.offset. + * Checksum simply sums -X for each byte X in the range + * using 8-bit math. + */ + struct { + char file[BIOS_LINKER_LOADER_FILESZ]; + uint32_t offset; + uint32_t start; + uint32_t length; + } cksum; + + /* padding */ + char pad[124]; + }; +} QEMU_PACKED; +typedef struct BiosLinkerLoaderEntry BiosLinkerLoaderEntry; + +enum { + BIOS_LINKER_LOADER_COMMAND_ALLOCATE = 0x1, + BIOS_LINKER_LOADER_COMMAND_ADD_POINTER = 0x2, + BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM = 0x3, +}; + +enum { + BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH = 0x1, + BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG = 0x2, +}; + +GArray *bios_linker_loader_init(void) +{ + return g_array_new(false, true /* clear */, sizeof(BiosLinkerLoaderEntry)); +} + +/* Free linker wrapper and return the linker array. */ +void *bios_linker_loader_cleanup(GArray *linker) +{ + return g_array_free(linker, false); +} + +void bios_linker_loader_alloc(GArray *linker, + const char *file, + uint32_t alloc_align, + bool alloc_fseg) +{ + BiosLinkerLoaderEntry entry; + + memset(&entry, 0, sizeof entry); + strncpy(entry.alloc.file, file, sizeof entry.alloc.file - 1); + entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ALLOCATE); + entry.alloc.align = cpu_to_le32(alloc_align); + entry.alloc.zone = cpu_to_le32(alloc_fseg ? + BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG : + BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH); + + /* Alloc entries must come first, so prepend them */ + g_array_prepend_val(linker, entry); +} + +void bios_linker_loader_add_checksum(GArray *linker, const char *file, + void *table, + void *start, unsigned size, + uint8_t *checksum) +{ + BiosLinkerLoaderEntry entry; + + memset(&entry, 0, sizeof entry); + strncpy(entry.cksum.file, file, sizeof entry.cksum.file - 1); + entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM); + entry.cksum.offset = cpu_to_le32(checksum - (uint8_t *)table); + entry.cksum.start = cpu_to_le32((uint8_t *)start - (uint8_t *)table); + entry.cksum.length = cpu_to_le32(size); + + g_array_append_val(linker, entry); +} + +void bios_linker_loader_add_pointer(GArray *linker, + const char *dest_file, + const char *src_file, + GArray *table, void *pointer, + uint8_t pointer_size) +{ + BiosLinkerLoaderEntry entry; + + memset(&entry, 0, sizeof entry); + strncpy(entry.pointer.dest_file, dest_file, + sizeof entry.pointer.dest_file - 1); + strncpy(entry.pointer.src_file, src_file, + sizeof entry.pointer.src_file - 1); + entry.command = cpu_to_le32(BIOS_LINKER_LOADER_COMMAND_ADD_POINTER); + entry.pointer.offset = cpu_to_le32((gchar *)pointer - table->data); + entry.pointer.size = pointer_size; + assert(pointer_size == 1 || pointer_size == 2 || + pointer_size == 4 || pointer_size == 8); + + g_array_append_val(linker, entry); +} diff --git a/hw/i386/bios-linker-loader.h b/hw/i386/bios-linker-loader.h new file mode 100644 index 0000000000..498c0af773 --- /dev/null +++ b/hw/i386/bios-linker-loader.h @@ -0,0 +1,27 @@ +#ifndef BIOS_LINKER_LOADER_H +#define BIOS_LINKER_LOADER_H + +#include <glib.h> +#include <stdbool.h> +#include <inttypes.h> + +GArray *bios_linker_loader_init(void); + +void bios_linker_loader_alloc(GArray *linker, + const char *file, + uint32_t alloc_align, + bool alloc_fseg); + +void bios_linker_loader_add_checksum(GArray *linker, const char *file, + void *table, + void *start, unsigned size, + uint8_t *checksum); + +void bios_linker_loader_add_pointer(GArray *linker, + const char *dest_file, + const char *src_file, + GArray *table, void *pointer, + uint8_t pointer_size); + +void *bios_linker_loader_cleanup(GArray *linker); +#endif diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c index 4e651107e1..968680104b 100644 --- a/hw/i386/kvm/pci-assign.c +++ b/hw/i386/kvm/pci-assign.c @@ -755,26 +755,22 @@ static void assign_failed_examine(AssignedDevice *dev) goto fail; } - error_report("*** The driver '%s' is occupying your device " - "%04x:%02x:%02x.%x.", - ns, dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function); - error_report("***"); - error_report("*** You can try the following commands to free it:"); - error_report("***"); - error_report("*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/" - "new_id", vendor_id, device_id); - error_report("*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/" - "%s/unbind", - dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function, ns); - error_report("*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/" - "pci-stub/bind", - dev->host.domain, dev->host.bus, dev->host.slot, - dev->host.function); - error_report("*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub" - "/remove_id", vendor_id, device_id); - error_report("***"); + error_printf("*** The driver '%s' is occupying your device " + "%04x:%02x:%02x.%x.\n" + "***\n" + "*** You can try the following commands to free it:\n" + "***\n" + "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/new_id\n" + "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/%s/unbind\n" + "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/" + "pci-stub/bind\n" + "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/remove_id\n" + "***", + ns, dev->host.domain, dev->host.bus, dev->host.slot, + dev->host.function, vendor_id, device_id, + dev->host.domain, dev->host.bus, dev->host.slot, dev->host.function, + ns, dev->host.domain, dev->host.bus, dev->host.slot, + dev->host.function, vendor_id, device_id); return; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 0c313feb0b..12c436e7f1 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -56,6 +56,7 @@ #include "hw/cpu/icc_bus.h" #include "hw/boards.h" #include "hw/pci/pci_host.h" +#include "acpi-build.h" /* debug PC/ISA interrupts */ //#define DEBUG_IRQ @@ -89,7 +90,9 @@ struct e820_table { struct e820_entry entry[E820_NR_ENTRIES]; } QEMU_PACKED __attribute((__aligned__(4))); -static struct e820_table e820_table; +static struct e820_table e820_reserve; +static struct e820_entry *e820_table; +static unsigned e820_entries; struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; void gsi_handler(void *opaque, int n, int level) @@ -576,19 +579,32 @@ static void handle_a20_line_change(void *opaque, int irq, int level) int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) { - int index = le32_to_cpu(e820_table.count); + int index = le32_to_cpu(e820_reserve.count); struct e820_entry *entry; - if (index >= E820_NR_ENTRIES) - return -EBUSY; - entry = &e820_table.entry[index++]; + if (type != E820_RAM) { + /* old FW_CFG_E820_TABLE entry -- reservations only */ + if (index >= E820_NR_ENTRIES) { + return -EBUSY; + } + entry = &e820_reserve.entry[index++]; + + entry->address = cpu_to_le64(address); + entry->length = cpu_to_le64(length); + entry->type = cpu_to_le32(type); + + e820_reserve.count = cpu_to_le32(index); + } - entry->address = cpu_to_le64(address); - entry->length = cpu_to_le64(length); - entry->type = cpu_to_le32(type); + /* new "etc/e820" file -- include ram too */ + e820_table = g_realloc(e820_table, + sizeof(struct e820_entry) * (e820_entries+1)); + e820_table[e820_entries].address = cpu_to_le64(address); + e820_table[e820_entries].length = cpu_to_le64(length); + e820_table[e820_entries].type = cpu_to_le32(type); + e820_entries++; - e820_table.count = cpu_to_le32(index); - return index; + return e820_entries; } /* Calculates the limit to CPU APIC ID values @@ -639,7 +655,9 @@ static FWCfgState *bochs_bios_init(void) fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, smbios_table, smbios_len); fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, - &e820_table, sizeof(e820_table)); + &e820_reserve, sizeof(e820_reserve)); + fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, + sizeof(struct e820_entry) * e820_entries); fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_cfg, sizeof(hpet_cfg)); /* allocate memory for the NUMA channel: one (64bit) word for the number @@ -1040,6 +1058,7 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data) PcGuestInfoState, machine_done); pc_fw_cfg_guest_info(&guest_info_state->info); + acpi_setup(&guest_info_state->info); } PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size, @@ -1047,6 +1066,27 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size, { PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state); PcGuestInfo *guest_info = &guest_info_state->info; + int i, j; + + guest_info->ram_size = below_4g_mem_size + above_4g_mem_size; + guest_info->apic_id_limit = pc_apic_id_limit(max_cpus); + guest_info->apic_xrupt_override = kvm_allows_irq0_override(); + guest_info->numa_nodes = nb_numa_nodes; + guest_info->node_mem = g_memdup(node_mem, guest_info->numa_nodes * + sizeof *guest_info->node_mem); + guest_info->node_cpu = g_malloc0(guest_info->apic_id_limit * + sizeof *guest_info->node_cpu); + + for (i = 0; i < max_cpus; i++) { + unsigned int apic_id = x86_cpu_apic_id_from_index(i); + assert(apic_id < guest_info->apic_id_limit); + for (j = 0; j < nb_numa_nodes; j++) { + if (test_bit(i, node_cpumask[j])) { + guest_info->node_cpu[apic_id] = j; + break; + } + } + } guest_info_state->machine_done.notify = pc_guest_info_machine_done; qemu_add_machine_init_done_notifier(&guest_info_state->machine_done); @@ -1093,7 +1133,7 @@ void pc_acpi_init(const char *default_dsdt) opts = qemu_opts_parse(qemu_find_opts("acpi"), arg, 0); g_assert(opts != NULL); - acpi_table_add(opts, &err); + acpi_table_add_builtin(opts, &err); if (err) { error_report("WARNING: failed to load %s: %s", filename, error_get_pretty(err)); @@ -1134,12 +1174,14 @@ FWCfgState *pc_memory_init(MemoryRegion *system_memory, memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram, 0, below_4g_mem_size); memory_region_add_subregion(system_memory, 0, ram_below_4g); + e820_add_entry(0, below_4g_mem_size, E820_RAM); if (above_4g_mem_size > 0) { ram_above_4g = g_malloc(sizeof(*ram_above_4g)); memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram, below_4g_mem_size, above_4g_mem_size); memory_region_add_subregion(system_memory, 0x100000000ULL, ram_above_4g); + e820_add_entry(0x100000000ULL, above_4g_mem_size, E820_RAM); } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index c6042c7e23..094c4212e6 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -58,7 +58,8 @@ static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; static bool has_pvpanic; -static bool has_pci_info = true; +static bool has_pci_info; +static bool has_acpi_build = true; /* PC hardware initialisation */ static void pc_init1(QEMUMachineInitArgs *args, @@ -122,6 +123,9 @@ static void pc_init1(QEMUMachineInitArgs *args, } guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size); + + guest_info->has_acpi_build = has_acpi_build; + guest_info->has_pci_info = has_pci_info; guest_info->isapc_ram_fw = !pci_enabled; @@ -240,6 +244,7 @@ static void pc_compat_1_6(QEMUMachineInitArgs *args) { has_pci_info = false; rom_file_in_ram = false; + has_acpi_build = false; } static void pc_compat_1_5(QEMUMachineInitArgs *args) @@ -304,6 +309,7 @@ static void pc_init_pci_1_2(QEMUMachineInitArgs *args) static void pc_init_pci_no_kvmclock(QEMUMachineInitArgs *args) { has_pci_info = false; + has_acpi_build = false; disable_kvm_pv_eoi(); enable_compat_apic_id_mode(); pc_init1(args, 1, 0); @@ -312,6 +318,7 @@ static void pc_init_pci_no_kvmclock(QEMUMachineInitArgs *args) static void pc_init_isa(QEMUMachineInitArgs *args) { has_pci_info = false; + has_acpi_build = false; if (!args->cpu_model) { args->cpu_model = "486"; } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index ca84e1c04c..1af8e2b943 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -48,7 +48,8 @@ #define MAX_SATA_PORTS 6 static bool has_pvpanic; -static bool has_pci_info = true; +static bool has_pci_info; +static bool has_acpi_build = true; /* PC hardware initialisation */ static void pc_q35_init(QEMUMachineInitArgs *args) @@ -111,6 +112,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args) guest_info = pc_guest_info_init(below_4g_mem_size, above_4g_mem_size); guest_info->has_pci_info = has_pci_info; guest_info->isapc_ram_fw = false; + guest_info->has_acpi_build = has_acpi_build; /* allocate ram and load rom/bios */ if (!xen_enabled()) { @@ -224,6 +226,7 @@ static void pc_compat_1_6(QEMUMachineInitArgs *args) { has_pci_info = false; rom_file_in_ram = false; + has_acpi_build = false; } static void pc_compat_1_5(QEMUMachineInitArgs *args) diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl new file mode 100644 index 0000000000..21c89b098b --- /dev/null +++ b/hw/i386/q35-acpi-dsdt.dsl @@ -0,0 +1,452 @@ +/* + * Bochs/QEMU ACPI DSDT ASL definition + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +/* + * Copyright (c) 2010 Isaku Yamahata + * yamahata at valinux co jp + * Based on acpi-dsdt.dsl, but heavily modified for q35 chipset. + */ + +ACPI_EXTRACT_ALL_CODE Q35AcpiDsdtAmlCode + +DefinitionBlock ( + "q35-acpi-dsdt.aml",// Output Filename + "DSDT", // Signature + 0x01, // DSDT Compliance Revision + "BXPC", // OEMID + "BXDSDT", // TABLE ID + 0x2 // OEM Revision + ) +{ + +#include "acpi-dsdt-dbug.dsl" + + Scope(\_SB) { + OperationRegion(PCST, SystemIO, 0xae00, 0x0c) + OperationRegion(PCSB, SystemIO, 0xae0c, 0x01) + Field(PCSB, AnyAcc, NoLock, WriteAsZeros) { + PCIB, 8, + } + } + + +/**************************************************************** + * PCI Bus definition + ****************************************************************/ + + Scope(\_SB) { + Device(PCI0) { + Name(_HID, EisaId("PNP0A08")) + Name(_CID, EisaId("PNP0A03")) + Name(_ADR, 0x00) + Name(_UID, 1) + + // _OSC: based on sample of ACPI3.0b spec + Name(SUPP, 0) // PCI _OSC Support Field value + Name(CTRL, 0) // PCI _OSC Control Field value + Method(_OSC, 4) { + // Create DWORD-addressable fields from the Capabilities Buffer + CreateDWordField(Arg3, 0, CDW1) + + // Check for proper UUID + If (LEqual(Arg0, ToUUID("33DB4D5B-1FF7-401C-9657-7441C03DD766"))) { + // Create DWORD-addressable fields from the Capabilities Buffer + CreateDWordField(Arg3, 4, CDW2) + CreateDWordField(Arg3, 8, CDW3) + + // Save Capabilities DWORD2 & 3 + Store(CDW2, SUPP) + Store(CDW3, CTRL) + + // Always allow native PME, AER (no dependencies) + // Never allow SHPC (no SHPC controller in this system) + And(CTRL, 0x1D, CTRL) + +#if 0 // For now, nothing to do + If (Not(And(CDW1, 1))) { // Query flag clear? + // Disable GPEs for features granted native control. + If (And(CTRL, 0x01)) { // Hot plug control granted? + Store(0, HPCE) // clear the hot plug SCI enable bit + Store(1, HPCS) // clear the hot plug SCI status bit + } + If (And(CTRL, 0x04)) { // PME control granted? + Store(0, PMCE) // clear the PME SCI enable bit + Store(1, PMCS) // clear the PME SCI status bit + } + If (And(CTRL, 0x10)) { // OS restoring PCI Express cap structure? + // Set status to not restore PCI Express cap structure + // upon resume from S3 + Store(1, S3CR) + } + } +#endif + If (LNotEqual(Arg1, One)) { + // Unknown revision + Or(CDW1, 0x08, CDW1) + } + If (LNotEqual(CDW3, CTRL)) { + // Capabilities bits were masked + Or(CDW1, 0x10, CDW1) + } + // Update DWORD3 in the buffer + Store(CTRL, CDW3) + } Else { + Or(CDW1, 4, CDW1) // Unrecognized UUID + } + Return (Arg3) + } + } + } + +#include "acpi-dsdt-pci-crs.dsl" +#include "acpi-dsdt-hpet.dsl" + + +/**************************************************************** + * VGA + ****************************************************************/ + + Scope(\_SB.PCI0) { + Device(VGA) { + Name(_ADR, 0x00010000) + Method(_S1D, 0, NotSerialized) { + Return (0x00) + } + Method(_S2D, 0, NotSerialized) { + Return (0x00) + } + Method(_S3D, 0, NotSerialized) { + Return (0x00) + } + } + } + + +/**************************************************************** + * LPC ISA bridge + ****************************************************************/ + + Scope(\_SB.PCI0) { + /* PCI D31:f0 LPC ISA bridge */ + Device(ISA) { + /* PCI D31:f0 */ + Name(_ADR, 0x001f0000) + + /* ICH9 PCI to ISA irq remapping */ + OperationRegion(PIRQ, PCI_Config, 0x60, 0x0C) + + OperationRegion(LPCD, PCI_Config, 0x80, 0x2) + Field(LPCD, AnyAcc, NoLock, Preserve) { + COMA, 3, + , 1, + COMB, 3, + + Offset(0x01), + LPTD, 2, + , 2, + FDCD, 2 + } + OperationRegion(LPCE, PCI_Config, 0x82, 0x2) + Field(LPCE, AnyAcc, NoLock, Preserve) { + CAEN, 1, + CBEN, 1, + LPEN, 1, + FDEN, 1 + } + } + } + +#include "acpi-dsdt-isa.dsl" + + +/**************************************************************** + * PCI IRQs + ****************************************************************/ + + /* Zero => PIC mode, One => APIC Mode */ + Name(\PICF, Zero) + Method(\_PIC, 1, NotSerialized) { + Store(Arg0, \PICF) + } + + Scope(\_SB) { + Scope(PCI0) { +#define prt_slot_lnk(nr, lnk0, lnk1, lnk2, lnk3) \ + Package() { nr##ffff, 0, lnk0, 0 }, \ + Package() { nr##ffff, 1, lnk1, 0 }, \ + Package() { nr##ffff, 2, lnk2, 0 }, \ + Package() { nr##ffff, 3, lnk3, 0 } + +#define prt_slot_lnkA(nr) prt_slot_lnk(nr, LNKA, LNKB, LNKC, LNKD) +#define prt_slot_lnkB(nr) prt_slot_lnk(nr, LNKB, LNKC, LNKD, LNKA) +#define prt_slot_lnkC(nr) prt_slot_lnk(nr, LNKC, LNKD, LNKA, LNKB) +#define prt_slot_lnkD(nr) prt_slot_lnk(nr, LNKD, LNKA, LNKB, LNKC) + +#define prt_slot_lnkE(nr) prt_slot_lnk(nr, LNKE, LNKF, LNKG, LNKH) +#define prt_slot_lnkF(nr) prt_slot_lnk(nr, LNKF, LNKG, LNKH, LNKE) +#define prt_slot_lnkG(nr) prt_slot_lnk(nr, LNKG, LNKH, LNKE, LNKF) +#define prt_slot_lnkH(nr) prt_slot_lnk(nr, LNKH, LNKE, LNKF, LNKG) + + Name(PRTP, package() { + prt_slot_lnkE(0x0000), + prt_slot_lnkF(0x0001), + prt_slot_lnkG(0x0002), + prt_slot_lnkH(0x0003), + prt_slot_lnkE(0x0004), + prt_slot_lnkF(0x0005), + prt_slot_lnkG(0x0006), + prt_slot_lnkH(0x0007), + prt_slot_lnkE(0x0008), + prt_slot_lnkF(0x0009), + prt_slot_lnkG(0x000a), + prt_slot_lnkH(0x000b), + prt_slot_lnkE(0x000c), + prt_slot_lnkF(0x000d), + prt_slot_lnkG(0x000e), + prt_slot_lnkH(0x000f), + prt_slot_lnkE(0x0010), + prt_slot_lnkF(0x0011), + prt_slot_lnkG(0x0012), + prt_slot_lnkH(0x0013), + prt_slot_lnkE(0x0014), + prt_slot_lnkF(0x0015), + prt_slot_lnkG(0x0016), + prt_slot_lnkH(0x0017), + prt_slot_lnkE(0x0018), + + /* INTA -> PIRQA for slot 25 - 31 + see the default value of D<N>IR */ + prt_slot_lnkA(0x0019), + prt_slot_lnkA(0x001a), + prt_slot_lnkA(0x001b), + prt_slot_lnkA(0x001c), + prt_slot_lnkA(0x001d), + + /* PCIe->PCI bridge. use PIRQ[E-H] */ + prt_slot_lnkE(0x001e), + + prt_slot_lnkA(0x001f) + }) + +#define prt_slot_gsi(nr, gsi0, gsi1, gsi2, gsi3) \ + Package() { nr##ffff, 0, gsi0, 0 }, \ + Package() { nr##ffff, 1, gsi1, 0 }, \ + Package() { nr##ffff, 2, gsi2, 0 }, \ + Package() { nr##ffff, 3, gsi3, 0 } + +#define prt_slot_gsiA(nr) prt_slot_gsi(nr, GSIA, GSIB, GSIC, GSID) +#define prt_slot_gsiB(nr) prt_slot_gsi(nr, GSIB, GSIC, GSID, GSIA) +#define prt_slot_gsiC(nr) prt_slot_gsi(nr, GSIC, GSID, GSIA, GSIB) +#define prt_slot_gsiD(nr) prt_slot_gsi(nr, GSID, GSIA, GSIB, GSIC) + +#define prt_slot_gsiE(nr) prt_slot_gsi(nr, GSIE, GSIF, GSIG, GSIH) +#define prt_slot_gsiF(nr) prt_slot_gsi(nr, GSIF, GSIG, GSIH, GSIE) +#define prt_slot_gsiG(nr) prt_slot_gsi(nr, GSIG, GSIH, GSIE, GSIF) +#define prt_slot_gsiH(nr) prt_slot_gsi(nr, GSIH, GSIE, GSIF, GSIG) + + Name(PRTA, package() { + prt_slot_gsiE(0x0000), + prt_slot_gsiF(0x0001), + prt_slot_gsiG(0x0002), + prt_slot_gsiH(0x0003), + prt_slot_gsiE(0x0004), + prt_slot_gsiF(0x0005), + prt_slot_gsiG(0x0006), + prt_slot_gsiH(0x0007), + prt_slot_gsiE(0x0008), + prt_slot_gsiF(0x0009), + prt_slot_gsiG(0x000a), + prt_slot_gsiH(0x000b), + prt_slot_gsiE(0x000c), + prt_slot_gsiF(0x000d), + prt_slot_gsiG(0x000e), + prt_slot_gsiH(0x000f), + prt_slot_gsiE(0x0010), + prt_slot_gsiF(0x0011), + prt_slot_gsiG(0x0012), + prt_slot_gsiH(0x0013), + prt_slot_gsiE(0x0014), + prt_slot_gsiF(0x0015), + prt_slot_gsiG(0x0016), + prt_slot_gsiH(0x0017), + prt_slot_gsiE(0x0018), + + /* INTA -> PIRQA for slot 25 - 31, but 30 + see the default value of D<N>IR */ + prt_slot_gsiA(0x0019), + prt_slot_gsiA(0x001a), + prt_slot_gsiA(0x001b), + prt_slot_gsiA(0x001c), + prt_slot_gsiA(0x001d), + + /* PCIe->PCI bridge. use PIRQ[E-H] */ + prt_slot_gsiE(0x001e), + + prt_slot_gsiA(0x001f) + }) + + Method(_PRT, 0, NotSerialized) { + /* PCI IRQ routing table, example from ACPI 2.0a specification, + section 6.2.8.1 */ + /* Note: we provide the same info as the PCI routing + table of the Bochs BIOS */ + If (LEqual(\PICF, Zero)) { + Return (PRTP) + } Else { + Return (PRTA) + } + } + } + + Field(PCI0.ISA.PIRQ, ByteAcc, NoLock, Preserve) { + PRQA, 8, + PRQB, 8, + PRQC, 8, + PRQD, 8, + + Offset(0x08), + PRQE, 8, + PRQF, 8, + PRQG, 8, + PRQH, 8 + } + + Method(IQST, 1, NotSerialized) { + // _STA method - get status + If (And(0x80, Arg0)) { + Return (0x09) + } + Return (0x0B) + } + Method(IQCR, 1, NotSerialized) { + // _CRS method - get current settings + Name(PRR0, ResourceTemplate() { + Interrupt(, Level, ActiveHigh, Shared) { 0 } + }) + CreateDWordField(PRR0, 0x05, PRRI) + Store(And(Arg0, 0x0F), PRRI) + Return (PRR0) + } + +#define define_link(link, uid, reg) \ + Device(link) { \ + Name(_HID, EISAID("PNP0C0F")) \ + Name(_UID, uid) \ + Name(_PRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + 5, 10, 11 \ + } \ + }) \ + Method(_STA, 0, NotSerialized) { \ + Return (IQST(reg)) \ + } \ + Method(_DIS, 0, NotSerialized) { \ + Or(reg, 0x80, reg) \ + } \ + Method(_CRS, 0, NotSerialized) { \ + Return (IQCR(reg)) \ + } \ + Method(_SRS, 1, NotSerialized) { \ + CreateDWordField(Arg0, 0x05, PRRI) \ + Store(PRRI, reg) \ + } \ + } + + define_link(LNKA, 0, PRQA) + define_link(LNKB, 1, PRQB) + define_link(LNKC, 2, PRQC) + define_link(LNKD, 3, PRQD) + define_link(LNKE, 4, PRQE) + define_link(LNKF, 5, PRQF) + define_link(LNKG, 6, PRQG) + define_link(LNKH, 7, PRQH) + +#define define_gsi_link(link, uid, gsi) \ + Device(link) { \ + Name(_HID, EISAID("PNP0C0F")) \ + Name(_UID, uid) \ + Name(_PRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + gsi \ + } \ + }) \ + Name(_CRS, ResourceTemplate() { \ + Interrupt(, Level, ActiveHigh, Shared) { \ + gsi \ + } \ + }) \ + Method(_SRS, 1, NotSerialized) { \ + } \ + } + + define_gsi_link(GSIA, 0, 0x10) + define_gsi_link(GSIB, 0, 0x11) + define_gsi_link(GSIC, 0, 0x12) + define_gsi_link(GSID, 0, 0x13) + define_gsi_link(GSIE, 0, 0x14) + define_gsi_link(GSIF, 0, 0x15) + define_gsi_link(GSIG, 0, 0x16) + define_gsi_link(GSIH, 0, 0x17) + } + +#include "acpi-dsdt-cpu-hotplug.dsl" + + +/**************************************************************** + * General purpose events + ****************************************************************/ + + Scope(\_GPE) { + Name(_HID, "ACPI0006") + + Method(_L00) { + } + Method(_L01) { + // CPU hotplug event + \_SB.PRSC() + } + Method(_L02) { + } + Method(_L03) { + } + Method(_L04) { + } + Method(_L05) { + } + Method(_L06) { + } + Method(_L07) { + } + Method(_L08) { + } + Method(_L09) { + } + Method(_L0A) { + } + Method(_L0B) { + } + Method(_L0C) { + } + Method(_L0D) { + } + Method(_L0E) { + } + Method(_L0F) { + } + } +} diff --git a/hw/i386/q35-acpi-dsdt.hex.generated b/hw/i386/q35-acpi-dsdt.hex.generated new file mode 100644 index 0000000000..32c16ff86f --- /dev/null +++ b/hw/i386/q35-acpi-dsdt.hex.generated @@ -0,0 +1,7346 @@ +static unsigned char Q35AcpiDsdtAmlCode[] = { +0x44, +0x53, +0x44, +0x54, +0xb0, +0x1c, +0x0, +0x0, +0x1, +0x6, +0x42, +0x58, +0x50, +0x43, +0x0, +0x0, +0x42, +0x58, +0x44, +0x53, +0x44, +0x54, +0x0, +0x0, +0x2, +0x0, +0x0, +0x0, +0x49, +0x4e, +0x54, +0x4c, +0x23, +0x8, +0x13, +0x20, +0x10, +0x49, +0x4, +0x5c, +0x0, +0x5b, +0x80, +0x44, +0x42, +0x47, +0x5f, +0x1, +0xb, +0x2, +0x4, +0x1, +0x5b, +0x81, +0xb, +0x44, +0x42, +0x47, +0x5f, +0x1, +0x44, +0x42, +0x47, +0x42, +0x8, +0x14, +0x2c, +0x44, +0x42, +0x55, +0x47, +0x1, +0x98, +0x68, +0x60, +0x96, +0x60, +0x60, +0x74, +0x87, +0x60, +0x1, +0x61, +0x70, +0x0, +0x62, +0xa2, +0x10, +0x95, +0x62, +0x61, +0x70, +0x83, +0x88, +0x60, +0x62, +0x0, +0x44, +0x42, +0x47, +0x42, +0x75, +0x62, +0x70, +0xa, +0xa, +0x44, +0x42, +0x47, +0x42, +0x10, +0x29, +0x5f, +0x53, +0x42, +0x5f, +0x5b, +0x80, +0x50, +0x43, +0x53, +0x54, +0x1, +0xb, +0x0, +0xae, +0xa, +0xc, +0x5b, +0x80, +0x50, +0x43, +0x53, +0x42, +0x1, +0xb, +0xc, +0xae, +0x1, +0x5b, +0x81, +0xb, +0x50, +0x43, +0x53, +0x42, +0x40, +0x50, +0x43, +0x49, +0x42, +0x8, +0x10, +0x4f, +0xc, +0x5f, +0x53, +0x42, +0x5f, +0x5b, +0x82, +0x47, +0xc, +0x50, +0x43, +0x49, +0x30, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xa, +0x8, +0x8, +0x5f, +0x43, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xa, +0x3, +0x8, +0x5f, +0x41, +0x44, +0x52, +0x0, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x1, +0x8, +0x53, +0x55, +0x50, +0x50, +0x0, +0x8, +0x43, +0x54, +0x52, +0x4c, +0x0, +0x14, +0x44, +0x9, +0x5f, +0x4f, +0x53, +0x43, +0x4, +0x8a, +0x6b, +0x0, +0x43, +0x44, +0x57, +0x31, +0xa0, +0x46, +0x7, +0x93, +0x68, +0x11, +0x13, +0xa, +0x10, +0x5b, +0x4d, +0xdb, +0x33, +0xf7, +0x1f, +0x1c, +0x40, +0x96, +0x57, +0x74, +0x41, +0xc0, +0x3d, +0xd7, +0x66, +0x8a, +0x6b, +0xa, +0x4, +0x43, +0x44, +0x57, +0x32, +0x8a, +0x6b, +0xa, +0x8, +0x43, +0x44, +0x57, +0x33, +0x70, +0x43, +0x44, +0x57, +0x32, +0x53, +0x55, +0x50, +0x50, +0x70, +0x43, +0x44, +0x57, +0x33, +0x43, +0x54, +0x52, +0x4c, +0x7b, +0x43, +0x54, +0x52, +0x4c, +0xa, +0x1d, +0x43, +0x54, +0x52, +0x4c, +0xa0, +0x10, +0x92, +0x93, +0x69, +0x1, +0x7d, +0x43, +0x44, +0x57, +0x31, +0xa, +0x8, +0x43, +0x44, +0x57, +0x31, +0xa0, +0x16, +0x92, +0x93, +0x43, +0x44, +0x57, +0x33, +0x43, +0x54, +0x52, +0x4c, +0x7d, +0x43, +0x44, +0x57, +0x31, +0xa, +0x10, +0x43, +0x44, +0x57, +0x31, +0x70, +0x43, +0x54, +0x52, +0x4c, +0x43, +0x44, +0x57, +0x33, +0xa1, +0xc, +0x7d, +0x43, +0x44, +0x57, +0x31, +0xa, +0x4, +0x43, +0x44, +0x57, +0x31, +0xa4, +0x6b, +0x10, +0x4e, +0x15, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x8, +0x43, +0x52, +0x45, +0x53, +0x11, +0x42, +0x7, +0xa, +0x6e, +0x88, +0xd, +0x0, +0x2, +0xc, +0x0, +0x0, +0x0, +0x0, +0x0, +0xff, +0x0, +0x0, +0x0, +0x0, +0x1, +0x47, +0x1, +0xf8, +0xc, +0xf8, +0xc, +0x1, +0x8, +0x88, +0xd, +0x0, +0x1, +0xc, +0x3, +0x0, +0x0, +0x0, +0x0, +0xf7, +0xc, +0x0, +0x0, +0xf8, +0xc, +0x88, +0xd, +0x0, +0x1, +0xc, +0x3, +0x0, +0x0, +0x0, +0xd, +0xff, +0xff, +0x0, +0x0, +0x0, +0xf3, +0x87, +0x17, +0x0, +0x0, +0xc, +0x3, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0xa, +0x0, +0xff, +0xff, +0xb, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x2, +0x0, +0x87, +0x17, +0x0, +0x0, +0xc, +0x1, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0xe0, +0xff, +0xff, +0xbf, +0xfe, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0xc0, +0x1e, +0x79, +0x0, +0x8, +0x43, +0x52, +0x36, +0x34, +0x11, +0x33, +0xa, +0x30, +0x8a, +0x2b, +0x0, +0x0, +0xc, +0x3, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x80, +0x0, +0x0, +0x0, +0xff, +0xff, +0xff, +0xff, +0xff, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x80, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x41, +0xa, +0x5f, +0x43, +0x52, +0x53, +0x0, +0x8a, +0x43, +0x52, +0x45, +0x53, +0xa, +0x5c, +0x50, +0x53, +0x33, +0x32, +0x8a, +0x43, +0x52, +0x45, +0x53, +0xa, +0x60, +0x50, +0x45, +0x33, +0x32, +0x8a, +0x43, +0x52, +0x45, +0x53, +0xa, +0x68, +0x50, +0x4c, +0x33, +0x32, +0x70, +0x50, +0x30, +0x53, +0x5f, +0x50, +0x53, +0x33, +0x32, +0x70, +0x50, +0x30, +0x45, +0x5f, +0x50, +0x45, +0x33, +0x32, +0x70, +0x72, +0x74, +0x50, +0x30, +0x45, +0x5f, +0x50, +0x30, +0x53, +0x5f, +0x0, +0x1, +0x0, +0x50, +0x4c, +0x33, +0x32, +0xa0, +0xc, +0x93, +0x50, +0x31, +0x56, +0x5f, +0x0, +0xa4, +0x43, +0x52, +0x45, +0x53, +0x8f, +0x43, +0x52, +0x36, +0x34, +0xa, +0xe, +0x50, +0x53, +0x36, +0x34, +0x8f, +0x43, +0x52, +0x36, +0x34, +0xa, +0x16, +0x50, +0x45, +0x36, +0x34, +0x8f, +0x43, +0x52, +0x36, +0x34, +0xa, +0x26, +0x50, +0x4c, +0x36, +0x34, +0x70, +0x50, +0x31, +0x53, +0x5f, +0x50, +0x53, +0x36, +0x34, +0x70, +0x50, +0x31, +0x45, +0x5f, +0x50, +0x45, +0x36, +0x34, +0x70, +0x50, +0x31, +0x4c, +0x5f, +0x50, +0x4c, +0x36, +0x34, +0x84, +0x43, +0x52, +0x45, +0x53, +0x43, +0x52, +0x36, +0x34, +0x60, +0xa4, +0x60, +0x10, +0x4d, +0x8, +0x5f, +0x53, +0x42, +0x5f, +0x5b, +0x82, +0x45, +0x8, +0x48, +0x50, +0x45, +0x54, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x1, +0x3, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x5b, +0x80, +0x48, +0x50, +0x54, +0x4d, +0x0, +0xc, +0x0, +0x0, +0xd0, +0xfe, +0xb, +0x0, +0x4, +0x5b, +0x81, +0x10, +0x48, +0x50, +0x54, +0x4d, +0x13, +0x56, +0x45, +0x4e, +0x44, +0x20, +0x50, +0x52, +0x44, +0x5f, +0x20, +0x14, +0x36, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x56, +0x45, +0x4e, +0x44, +0x60, +0x70, +0x50, +0x52, +0x44, +0x5f, +0x61, +0x7a, +0x60, +0xa, +0x10, +0x60, +0xa0, +0xc, +0x91, +0x93, +0x60, +0x0, +0x93, +0x60, +0xb, +0xff, +0xff, +0xa4, +0x0, +0xa0, +0xe, +0x91, +0x93, +0x61, +0x0, +0x94, +0x61, +0xc, +0x0, +0xe1, +0xf5, +0x5, +0xa4, +0x0, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x11, +0xa, +0xe, +0x86, +0x9, +0x0, +0x0, +0x0, +0x0, +0xd0, +0xfe, +0x0, +0x4, +0x0, +0x0, +0x79, +0x0, +0x10, +0x36, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x5b, +0x82, +0x2a, +0x56, +0x47, +0x41, +0x5f, +0x8, +0x5f, +0x41, +0x44, +0x52, +0xc, +0x0, +0x0, +0x1, +0x0, +0x14, +0x8, +0x5f, +0x53, +0x31, +0x44, +0x0, +0xa4, +0x0, +0x14, +0x8, +0x5f, +0x53, +0x32, +0x44, +0x0, +0xa4, +0x0, +0x14, +0x8, +0x5f, +0x53, +0x33, +0x44, +0x0, +0xa4, +0x0, +0x10, +0x4c, +0x7, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x5b, +0x82, +0x4f, +0x6, +0x49, +0x53, +0x41, +0x5f, +0x8, +0x5f, +0x41, +0x44, +0x52, +0xc, +0x0, +0x0, +0x1f, +0x0, +0x5b, +0x80, +0x50, +0x49, +0x52, +0x51, +0x2, +0xa, +0x60, +0xa, +0xc, +0x5b, +0x80, +0x4c, +0x50, +0x43, +0x44, +0x2, +0xa, +0x80, +0xa, +0x2, +0x5b, +0x81, +0x20, +0x4c, +0x50, +0x43, +0x44, +0x0, +0x43, +0x4f, +0x4d, +0x41, +0x3, +0x0, +0x1, +0x43, +0x4f, +0x4d, +0x42, +0x3, +0x0, +0x1, +0x4c, +0x50, +0x54, +0x44, +0x2, +0x0, +0x2, +0x46, +0x44, +0x43, +0x44, +0x2, +0x5b, +0x80, +0x4c, +0x50, +0x43, +0x45, +0x2, +0xa, +0x82, +0xa, +0x2, +0x5b, +0x81, +0x1a, +0x4c, +0x50, +0x43, +0x45, +0x0, +0x43, +0x41, +0x45, +0x4e, +0x1, +0x43, +0x42, +0x45, +0x4e, +0x1, +0x4c, +0x50, +0x45, +0x4e, +0x1, +0x46, +0x44, +0x45, +0x4e, +0x1, +0x10, +0x4c, +0x1b, +0x2f, +0x3, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x49, +0x53, +0x41, +0x5f, +0x5b, +0x82, +0x2d, +0x52, +0x54, +0x43, +0x5f, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xb, +0x0, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x18, +0xa, +0x15, +0x47, +0x1, +0x70, +0x0, +0x70, +0x0, +0x10, +0x2, +0x22, +0x0, +0x1, +0x47, +0x1, +0x72, +0x0, +0x72, +0x0, +0x2, +0x6, +0x79, +0x0, +0x5b, +0x82, +0x37, +0x4b, +0x42, +0x44, +0x5f, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x3, +0x3, +0x14, +0x9, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x18, +0xa, +0x15, +0x47, +0x1, +0x60, +0x0, +0x60, +0x0, +0x1, +0x1, +0x47, +0x1, +0x64, +0x0, +0x64, +0x0, +0x1, +0x1, +0x22, +0x2, +0x0, +0x79, +0x0, +0x5b, +0x82, +0x27, +0x4d, +0x4f, +0x55, +0x5f, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xf, +0x13, +0x14, +0x9, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x8, +0xa, +0x5, +0x22, +0x0, +0x10, +0x79, +0x0, +0x5b, +0x82, +0x4a, +0x4, +0x46, +0x44, +0x43, +0x30, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x7, +0x0, +0x14, +0x18, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x46, +0x44, +0x45, +0x4e, +0x60, +0xa0, +0x6, +0x93, +0x60, +0x0, +0xa4, +0x0, +0xa1, +0x4, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x1b, +0xa, +0x18, +0x47, +0x1, +0xf2, +0x3, +0xf2, +0x3, +0x0, +0x4, +0x47, +0x1, +0xf7, +0x3, +0xf7, +0x3, +0x0, +0x1, +0x22, +0x40, +0x0, +0x2a, +0x4, +0x0, +0x79, +0x0, +0x5b, +0x82, +0x3e, +0x4c, +0x50, +0x54, +0x5f, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x4, +0x0, +0x14, +0x18, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x4c, +0x50, +0x45, +0x4e, +0x60, +0xa0, +0x6, +0x93, +0x60, +0x0, +0xa4, +0x0, +0xa1, +0x4, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x10, +0xa, +0xd, +0x47, +0x1, +0x78, +0x3, +0x78, +0x3, +0x8, +0x8, +0x22, +0x80, +0x0, +0x79, +0x0, +0x5b, +0x82, +0x45, +0x4, +0x43, +0x4f, +0x4d, +0x31, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x5, +0x1, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x1, +0x14, +0x18, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x43, +0x41, +0x45, +0x4e, +0x60, +0xa0, +0x6, +0x93, +0x60, +0x0, +0xa4, +0x0, +0xa1, +0x4, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x10, +0xa, +0xd, +0x47, +0x1, +0xf8, +0x3, +0xf8, +0x3, +0x0, +0x8, +0x22, +0x10, +0x0, +0x79, +0x0, +0x5b, +0x82, +0x46, +0x4, +0x43, +0x4f, +0x4d, +0x32, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0x5, +0x1, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x2, +0x14, +0x18, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x43, +0x42, +0x45, +0x4e, +0x60, +0xa0, +0x6, +0x93, +0x60, +0x0, +0xa4, +0x0, +0xa1, +0x4, +0xa4, +0xa, +0xf, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x10, +0xa, +0xd, +0x47, +0x1, +0xf8, +0x2, +0xf8, +0x2, +0x0, +0x8, +0x22, +0x8, +0x0, +0x79, +0x0, +0x8, +0x50, +0x49, +0x43, +0x46, +0x0, +0x14, +0xc, +0x5f, +0x50, +0x49, +0x43, +0x1, +0x70, +0x68, +0x50, +0x49, +0x43, +0x46, +0x10, +0x8e, +0x55, +0x1, +0x5f, +0x53, +0x42, +0x5f, +0x10, +0x43, +0xea, +0x50, +0x43, +0x49, +0x30, +0x8, +0x50, +0x52, +0x54, +0x50, +0x12, +0x4b, +0x73, +0x80, +0x12, +0xb, +0x4, +0xb, +0xff, +0xff, +0x0, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xb, +0x4, +0xb, +0xff, +0xff, +0x1, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xc, +0x4, +0xb, +0xff, +0xff, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xc, +0x4, +0xb, +0xff, +0xff, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0x0, +0x4c, +0x4e, +0x4b, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0x1, +0x4c, +0x4e, +0x4b, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0xa, +0x2, +0x4c, +0x4e, +0x4b, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0xa, +0x3, +0x4c, +0x4e, +0x4b, +0x44, +0x0, +0x8, +0x50, +0x52, +0x54, +0x41, +0x12, +0x4b, +0x73, +0x80, +0x12, +0xb, +0x4, +0xb, +0xff, +0xff, +0x0, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xb, +0x4, +0xb, +0xff, +0xff, +0x1, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xc, +0x4, +0xb, +0xff, +0xff, +0xa, +0x2, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xc, +0x4, +0xb, +0xff, +0xff, +0xa, +0x3, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0x0, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0x1, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0x0, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0x1, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x2, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0x0, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0x1, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x3, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0x0, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0x1, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x4, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0x0, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0x1, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x5, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0x0, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0x1, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x6, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0x0, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0x1, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x7, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0x0, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0x1, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x8, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0x0, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0x1, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x9, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0x0, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0x1, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xa, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0x0, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0x1, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xb, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0x0, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0x1, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xc, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0x0, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0x1, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xd, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0x0, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0x1, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xe, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0x0, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0x1, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0xf, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0x0, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0x1, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x10, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0x0, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0x1, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x11, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0x0, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0x1, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x12, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0x0, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0x1, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x13, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0x0, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0x1, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x14, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0x0, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0x1, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x15, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0x0, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0x1, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x16, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0x0, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0x1, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x17, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0x0, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0x1, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x18, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0x0, +0x47, +0x53, +0x49, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0x1, +0x47, +0x53, +0x49, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x19, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0x0, +0x47, +0x53, +0x49, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0x1, +0x47, +0x53, +0x49, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1a, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0x0, +0x47, +0x53, +0x49, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0x1, +0x47, +0x53, +0x49, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1b, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0x0, +0x47, +0x53, +0x49, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0x1, +0x47, +0x53, +0x49, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1c, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0x0, +0x47, +0x53, +0x49, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0x1, +0x47, +0x53, +0x49, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1d, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x44, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0x0, +0x47, +0x53, +0x49, +0x45, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0x1, +0x47, +0x53, +0x49, +0x46, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x47, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1e, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x48, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0x0, +0x47, +0x53, +0x49, +0x41, +0x0, +0x12, +0xd, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0x1, +0x47, +0x53, +0x49, +0x42, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0xa, +0x2, +0x47, +0x53, +0x49, +0x43, +0x0, +0x12, +0xe, +0x4, +0xc, +0xff, +0xff, +0x1f, +0x0, +0xa, +0x3, +0x47, +0x53, +0x49, +0x44, +0x0, +0x14, +0x1a, +0x5f, +0x50, +0x52, +0x54, +0x0, +0xa0, +0xc, +0x93, +0x50, +0x49, +0x43, +0x46, +0x0, +0xa4, +0x50, +0x52, +0x54, +0x50, +0xa1, +0x6, +0xa4, +0x50, +0x52, +0x54, +0x41, +0x5b, +0x81, +0x3a, +0x2f, +0x3, +0x50, +0x43, +0x49, +0x30, +0x49, +0x53, +0x41, +0x5f, +0x50, +0x49, +0x52, +0x51, +0x1, +0x50, +0x52, +0x51, +0x41, +0x8, +0x50, +0x52, +0x51, +0x42, +0x8, +0x50, +0x52, +0x51, +0x43, +0x8, +0x50, +0x52, +0x51, +0x44, +0x8, +0x0, +0x20, +0x50, +0x52, +0x51, +0x45, +0x8, +0x50, +0x52, +0x51, +0x46, +0x8, +0x50, +0x52, +0x51, +0x47, +0x8, +0x50, +0x52, +0x51, +0x48, +0x8, +0x14, +0x13, +0x49, +0x51, +0x53, +0x54, +0x1, +0xa0, +0x9, +0x7b, +0xa, +0x80, +0x68, +0x0, +0xa4, +0xa, +0x9, +0xa4, +0xa, +0xb, +0x14, +0x34, +0x49, +0x51, +0x43, +0x52, +0x1, +0x8, +0x50, +0x52, +0x52, +0x30, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x0, +0x0, +0x0, +0x0, +0x79, +0x0, +0x8a, +0x50, +0x52, +0x52, +0x30, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x7b, +0x68, +0xa, +0xf, +0x0, +0x50, +0x52, +0x52, +0x49, +0xa4, +0x50, +0x52, +0x52, +0x30, +0x5b, +0x82, +0x4c, +0x7, +0x4c, +0x4e, +0x4b, +0x41, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x41, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x41, +0xa, +0x80, +0x50, +0x52, +0x51, +0x41, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x41, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x41, +0x5b, +0x82, +0x4c, +0x7, +0x4c, +0x4e, +0x4b, +0x42, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x1, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x42, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x42, +0xa, +0x80, +0x50, +0x52, +0x51, +0x42, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x42, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x42, +0x5b, +0x82, +0x4d, +0x7, +0x4c, +0x4e, +0x4b, +0x43, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x2, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x43, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x43, +0xa, +0x80, +0x50, +0x52, +0x51, +0x43, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x43, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x43, +0x5b, +0x82, +0x4d, +0x7, +0x4c, +0x4e, +0x4b, +0x44, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x3, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x44, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x44, +0xa, +0x80, +0x50, +0x52, +0x51, +0x44, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x44, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x44, +0x5b, +0x82, +0x4d, +0x7, +0x4c, +0x4e, +0x4b, +0x45, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x4, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x45, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x45, +0xa, +0x80, +0x50, +0x52, +0x51, +0x45, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x45, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x45, +0x5b, +0x82, +0x4d, +0x7, +0x4c, +0x4e, +0x4b, +0x46, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x5, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x46, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x46, +0xa, +0x80, +0x50, +0x52, +0x51, +0x46, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x46, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x46, +0x5b, +0x82, +0x4d, +0x7, +0x4c, +0x4e, +0x4b, +0x47, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x6, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x47, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x47, +0xa, +0x80, +0x50, +0x52, +0x51, +0x47, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x47, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x47, +0x5b, +0x82, +0x4d, +0x7, +0x4c, +0x4e, +0x4b, +0x48, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0xa, +0x7, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0x16, +0xa, +0x13, +0x89, +0xe, +0x0, +0x9, +0x3, +0x5, +0x0, +0x0, +0x0, +0xa, +0x0, +0x0, +0x0, +0xb, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x49, +0x51, +0x53, +0x54, +0x50, +0x52, +0x51, +0x48, +0x14, +0x11, +0x5f, +0x44, +0x49, +0x53, +0x0, +0x7d, +0x50, +0x52, +0x51, +0x48, +0xa, +0x80, +0x50, +0x52, +0x51, +0x48, +0x14, +0xf, +0x5f, +0x43, +0x52, +0x53, +0x0, +0xa4, +0x49, +0x51, +0x43, +0x52, +0x50, +0x52, +0x51, +0x48, +0x14, +0x17, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x8a, +0x68, +0xa, +0x5, +0x50, +0x52, +0x52, +0x49, +0x70, +0x50, +0x52, +0x52, +0x49, +0x50, +0x52, +0x51, +0x48, +0x5b, +0x82, +0x45, +0x4, +0x47, +0x53, +0x49, +0x41, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x10, +0x0, +0x0, +0x0, +0x79, +0x0, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x10, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x6, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x5b, +0x82, +0x45, +0x4, +0x47, +0x53, +0x49, +0x42, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x11, +0x0, +0x0, +0x0, +0x79, +0x0, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x11, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x6, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x5b, +0x82, +0x45, +0x4, +0x47, +0x53, +0x49, +0x43, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x12, +0x0, +0x0, +0x0, +0x79, +0x0, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x12, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x6, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x5b, +0x82, +0x45, +0x4, +0x47, +0x53, +0x49, +0x44, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x13, +0x0, +0x0, +0x0, +0x79, +0x0, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x13, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x6, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x5b, +0x82, +0x45, +0x4, +0x47, +0x53, +0x49, +0x45, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x14, +0x0, +0x0, +0x0, +0x79, +0x0, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x14, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x6, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x5b, +0x82, +0x45, +0x4, +0x47, +0x53, +0x49, +0x46, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x15, +0x0, +0x0, +0x0, +0x79, +0x0, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x15, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x6, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x5b, +0x82, +0x45, +0x4, +0x47, +0x53, +0x49, +0x47, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x16, +0x0, +0x0, +0x0, +0x79, +0x0, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x16, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x6, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x5b, +0x82, +0x45, +0x4, +0x47, +0x53, +0x49, +0x48, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x41, +0xd0, +0xc, +0xf, +0x8, +0x5f, +0x55, +0x49, +0x44, +0x0, +0x8, +0x5f, +0x50, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x17, +0x0, +0x0, +0x0, +0x79, +0x0, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0xe, +0xa, +0xb, +0x89, +0x6, +0x0, +0x9, +0x1, +0x17, +0x0, +0x0, +0x0, +0x79, +0x0, +0x14, +0x6, +0x5f, +0x53, +0x52, +0x53, +0x1, +0x10, +0x47, +0xe, +0x5f, +0x53, +0x42, +0x5f, +0x14, +0x35, +0x43, +0x50, +0x4d, +0x41, +0x1, +0x70, +0x83, +0x88, +0x43, +0x50, +0x4f, +0x4e, +0x68, +0x0, +0x60, +0x70, +0x11, +0xb, +0xa, +0x8, +0x0, +0x8, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x61, +0x70, +0x68, +0x88, +0x61, +0xa, +0x2, +0x0, +0x70, +0x68, +0x88, +0x61, +0xa, +0x3, +0x0, +0x70, +0x60, +0x88, +0x61, +0xa, +0x4, +0x0, +0xa4, +0x61, +0x14, +0x1a, +0x43, +0x50, +0x53, +0x54, +0x1, +0x70, +0x83, +0x88, +0x43, +0x50, +0x4f, +0x4e, +0x68, +0x0, +0x60, +0xa0, +0x5, +0x60, +0xa4, +0xa, +0xf, +0xa1, +0x3, +0xa4, +0x0, +0x14, +0xa, +0x43, +0x50, +0x45, +0x4a, +0x2, +0x5b, +0x22, +0xa, +0xc8, +0x5b, +0x80, +0x50, +0x52, +0x53, +0x54, +0x1, +0xb, +0x0, +0xaf, +0xa, +0x20, +0x5b, +0x81, +0xc, +0x50, +0x52, +0x53, +0x54, +0x1, +0x50, +0x52, +0x53, +0x5f, +0x40, +0x10, +0x14, +0x4a, +0x6, +0x50, +0x52, +0x53, +0x43, +0x0, +0x70, +0x50, +0x52, +0x53, +0x5f, +0x65, +0x70, +0x0, +0x62, +0x70, +0x0, +0x60, +0xa2, +0x46, +0x5, +0x95, +0x60, +0x87, +0x43, +0x50, +0x4f, +0x4e, +0x70, +0x83, +0x88, +0x43, +0x50, +0x4f, +0x4e, +0x60, +0x0, +0x61, +0xa0, +0xa, +0x7b, +0x60, +0xa, +0x7, +0x0, +0x7a, +0x62, +0x1, +0x62, +0xa1, +0xc, +0x70, +0x83, +0x88, +0x65, +0x7a, +0x60, +0xa, +0x3, +0x0, +0x0, +0x62, +0x70, +0x7b, +0x62, +0x1, +0x0, +0x63, +0xa0, +0x22, +0x92, +0x93, +0x61, +0x63, +0x70, +0x63, +0x88, +0x43, +0x50, +0x4f, +0x4e, +0x60, +0x0, +0xa0, +0xa, +0x93, +0x63, +0x1, +0x4e, +0x54, +0x46, +0x59, +0x60, +0x1, +0xa1, +0x8, +0x4e, +0x54, +0x46, +0x59, +0x60, +0xa, +0x3, +0x75, +0x60, +0x10, +0x4f, +0x8, +0x5f, +0x47, +0x50, +0x45, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xd, +0x41, +0x43, +0x50, +0x49, +0x30, +0x30, +0x30, +0x36, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x30, +0x0, +0x14, +0x10, +0x5f, +0x4c, +0x30, +0x31, +0x0, +0x5c, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x52, +0x53, +0x43, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x32, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x33, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x34, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x35, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x36, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x37, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x38, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x39, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x41, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x42, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x43, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x44, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x45, +0x0, +0x14, +0x6, +0x5f, +0x4c, +0x30, +0x46, +0x0 +}; diff --git a/hw/i386/ssdt-misc.dsl b/hw/i386/ssdt-misc.dsl new file mode 100644 index 0000000000..a4484b8176 --- /dev/null +++ b/hw/i386/ssdt-misc.dsl @@ -0,0 +1,119 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +ACPI_EXTRACT_ALL_CODE ssdp_misc_aml + +DefinitionBlock ("ssdt-misc.aml", "SSDT", 0x01, "BXPC", "BXSSDTSUSP", 0x1) +{ + +/**************************************************************** + * PCI memory ranges + ****************************************************************/ + + Scope(\) { + ACPI_EXTRACT_NAME_DWORD_CONST acpi_pci32_start + Name(P0S, 0x12345678) + ACPI_EXTRACT_NAME_DWORD_CONST acpi_pci32_end + Name(P0E, 0x12345678) + ACPI_EXTRACT_NAME_BYTE_CONST acpi_pci64_valid + Name(P1V, 0x12) + ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_start + Name(P1S, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) + ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_end + Name(P1E, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) + ACPI_EXTRACT_NAME_BUFFER8 acpi_pci64_length + Name(P1L, Buffer() { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }) + } + + +/**************************************************************** + * Suspend + ****************************************************************/ + + Scope(\) { + /* + * S3 (suspend-to-ram), S4 (suspend-to-disk) and S5 (power-off) type codes: + * must match piix4 emulation. + */ + + ACPI_EXTRACT_NAME_STRING acpi_s3_name + Name(_S3, Package(0x04) { + One, /* PM1a_CNT.SLP_TYP */ + One, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + ACPI_EXTRACT_NAME_STRING acpi_s4_name + ACPI_EXTRACT_PKG_START acpi_s4_pkg + Name(_S4, Package(0x04) { + 0x2, /* PM1a_CNT.SLP_TYP */ + 0x2, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + Name(_S5, Package(0x04) { + Zero, /* PM1a_CNT.SLP_TYP */ + Zero, /* PM1b_CNT.SLP_TYP */ + Zero, /* reserved */ + Zero /* reserved */ + }) + } + + External(\_SB.PCI0, DeviceObj) + External(\_SB.PCI0.ISA, DeviceObj) + + Scope(\_SB.PCI0.ISA) { + Device(PEVT) { + Name(_HID, "QEMU0001") + /* PEST will be patched to be Zero if no such device */ + ACPI_EXTRACT_NAME_WORD_CONST ssdt_isa_pest + Name(PEST, 0xFFFF) + OperationRegion(PEOR, SystemIO, PEST, 0x01) + Field(PEOR, ByteAcc, NoLock, Preserve) { + PEPT, 8, + } + + Method(_STA, 0, NotSerialized) { + Store(PEST, Local0) + If (LEqual(Local0, Zero)) { + Return (0x00) + } Else { + Return (0x0F) + } + } + + Method(RDPT, 0, NotSerialized) { + Store(PEPT, Local0) + Return (Local0) + } + + Method(WRPT, 1, NotSerialized) { + Store(Arg0, PEPT) + } + + Name(_CRS, ResourceTemplate() { + IO(Decode16, 0x00, 0x00, 0x01, 0x01, IO) + }) + + CreateWordField(_CRS, IO._MIN, IOMN) + CreateWordField(_CRS, IO._MAX, IOMX) + + Method(_INI, 0, NotSerialized) { + Store(PEST, IOMN) + Store(PEST, IOMX) + } + } + } +} diff --git a/hw/i386/ssdt-misc.hex.generated b/hw/i386/ssdt-misc.hex.generated new file mode 100644 index 0000000000..55e3bd2aa6 --- /dev/null +++ b/hw/i386/ssdt-misc.hex.generated @@ -0,0 +1,386 @@ +static unsigned char acpi_pci64_length[] = { +0x6f +}; +static unsigned char acpi_s4_pkg[] = { +0x8f +}; +static unsigned char acpi_s3_name[] = { +0x7c +}; +static unsigned char acpi_pci32_start[] = { +0x2f +}; +static unsigned char acpi_pci64_valid[] = { +0x43 +}; +static unsigned char ssdp_misc_aml[] = { +0x53, +0x53, +0x44, +0x54, +0x62, +0x1, +0x0, +0x0, +0x1, +0x76, +0x42, +0x58, +0x50, +0x43, +0x0, +0x0, +0x42, +0x58, +0x53, +0x53, +0x44, +0x54, +0x53, +0x55, +0x1, +0x0, +0x0, +0x0, +0x49, +0x4e, +0x54, +0x4c, +0x23, +0x8, +0x13, +0x20, +0x10, +0x42, +0x5, +0x5c, +0x0, +0x8, +0x50, +0x30, +0x53, +0x5f, +0xc, +0x78, +0x56, +0x34, +0x12, +0x8, +0x50, +0x30, +0x45, +0x5f, +0xc, +0x78, +0x56, +0x34, +0x12, +0x8, +0x50, +0x31, +0x56, +0x5f, +0xa, +0x12, +0x8, +0x50, +0x31, +0x53, +0x5f, +0x11, +0xb, +0xa, +0x8, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x8, +0x50, +0x31, +0x45, +0x5f, +0x11, +0xb, +0xa, +0x8, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x8, +0x50, +0x31, +0x4c, +0x5f, +0x11, +0xb, +0xa, +0x8, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x0, +0x10, +0x29, +0x5c, +0x0, +0x8, +0x5f, +0x53, +0x33, +0x5f, +0x12, +0x6, +0x4, +0x1, +0x1, +0x0, +0x0, +0x8, +0x5f, +0x53, +0x34, +0x5f, +0x12, +0x8, +0x4, +0xa, +0x2, +0xa, +0x2, +0x0, +0x0, +0x8, +0x5f, +0x53, +0x35, +0x5f, +0x12, +0x6, +0x4, +0x0, +0x0, +0x0, +0x0, +0x10, +0x40, +0xc, +0x5c, +0x2f, +0x3, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x49, +0x53, +0x41, +0x5f, +0x5b, +0x82, +0x4d, +0xa, +0x50, +0x45, +0x56, +0x54, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xd, +0x51, +0x45, +0x4d, +0x55, +0x30, +0x30, +0x30, +0x31, +0x0, +0x8, +0x50, +0x45, +0x53, +0x54, +0xb, +0xff, +0xff, +0x5b, +0x80, +0x50, +0x45, +0x4f, +0x52, +0x1, +0x50, +0x45, +0x53, +0x54, +0x1, +0x5b, +0x81, +0xb, +0x50, +0x45, +0x4f, +0x52, +0x1, +0x50, +0x45, +0x50, +0x54, +0x8, +0x14, +0x18, +0x5f, +0x53, +0x54, +0x41, +0x0, +0x70, +0x50, +0x45, +0x53, +0x54, +0x60, +0xa0, +0x6, +0x93, +0x60, +0x0, +0xa4, +0x0, +0xa1, +0x4, +0xa4, +0xa, +0xf, +0x14, +0xe, +0x52, +0x44, +0x50, +0x54, +0x0, +0x70, +0x50, +0x45, +0x50, +0x54, +0x60, +0xa4, +0x60, +0x14, +0xc, +0x57, +0x52, +0x50, +0x54, +0x1, +0x70, +0x68, +0x50, +0x45, +0x50, +0x54, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0xd, +0xa, +0xa, +0x47, +0x1, +0x0, +0x0, +0x0, +0x0, +0x1, +0x1, +0x79, +0x0, +0x8b, +0x5f, +0x43, +0x52, +0x53, +0xa, +0x2, +0x49, +0x4f, +0x4d, +0x4e, +0x8b, +0x5f, +0x43, +0x52, +0x53, +0xa, +0x4, +0x49, +0x4f, +0x4d, +0x58, +0x14, +0x18, +0x5f, +0x49, +0x4e, +0x49, +0x0, +0x70, +0x50, +0x45, +0x53, +0x54, +0x49, +0x4f, +0x4d, +0x4e, +0x70, +0x50, +0x45, +0x53, +0x54, +0x49, +0x4f, +0x4d, +0x58 +}; +static unsigned char ssdt_isa_pest[] = { +0xd0 +}; +static unsigned char acpi_s4_name[] = { +0x88 +}; +static unsigned char acpi_pci64_start[] = { +0x4d +}; +static unsigned char acpi_pci64_end[] = { +0x5e +}; +static unsigned char acpi_pci32_end[] = { +0x39 +}; diff --git a/hw/i386/ssdt-pcihp.dsl b/hw/i386/ssdt-pcihp.dsl new file mode 100644 index 0000000000..d29a5b95d2 --- /dev/null +++ b/hw/i386/ssdt-pcihp.dsl @@ -0,0 +1,51 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +ACPI_EXTRACT_ALL_CODE ssdp_pcihp_aml + +DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1) +{ + +/**************************************************************** + * PCI hotplug + ****************************************************************/ + + /* Objects supplied by DSDT */ + External(\_SB.PCI0, DeviceObj) + External(\_SB.PCI0.PCEJ, MethodObj) + + Scope(\_SB.PCI0) { + + /* Bulk generated PCI hotplug devices */ + ACPI_EXTRACT_DEVICE_START ssdt_pcihp_start + ACPI_EXTRACT_DEVICE_END ssdt_pcihp_end + ACPI_EXTRACT_DEVICE_STRING ssdt_pcihp_name + + // Method _EJ0 can be patched by BIOS to EJ0_ + // at runtime, if the slot is detected to not support hotplug. + // Extract the offset of the address dword and the + // _EJ0 name to allow this patching. + Device(SAA) { + ACPI_EXTRACT_NAME_BYTE_CONST ssdt_pcihp_id + Name(_SUN, 0xAA) + ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pcihp_adr + Name(_ADR, 0xAA0000) + ACPI_EXTRACT_METHOD_STRING ssdt_pcihp_ej0 + Method(_EJ0, 1) { + Return (PCEJ(_SUN)) + } + } + } +} diff --git a/hw/i386/ssdt-pcihp.hex.generated b/hw/i386/ssdt-pcihp.hex.generated new file mode 100644 index 0000000000..b3c2cd5cf9 --- /dev/null +++ b/hw/i386/ssdt-pcihp.hex.generated @@ -0,0 +1,108 @@ +static unsigned char ssdt_pcihp_name[] = { +0x33 +}; +static unsigned char ssdt_pcihp_adr[] = { +0x44 +}; +static unsigned char ssdt_pcihp_end[] = { +0x58 +}; +static unsigned char ssdp_pcihp_aml[] = { +0x53, +0x53, +0x44, +0x54, +0x58, +0x0, +0x0, +0x0, +0x1, +0x76, +0x42, +0x58, +0x50, +0x43, +0x0, +0x0, +0x42, +0x58, +0x53, +0x53, +0x44, +0x54, +0x50, +0x43, +0x1, +0x0, +0x0, +0x0, +0x49, +0x4e, +0x54, +0x4c, +0x23, +0x8, +0x13, +0x20, +0x10, +0x33, +0x5c, +0x2e, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x5b, +0x82, +0x26, +0x53, +0x41, +0x41, +0x5f, +0x8, +0x5f, +0x53, +0x55, +0x4e, +0xa, +0xaa, +0x8, +0x5f, +0x41, +0x44, +0x52, +0xc, +0x0, +0x0, +0xaa, +0x0, +0x14, +0xf, +0x5f, +0x45, +0x4a, +0x30, +0x1, +0xa4, +0x50, +0x43, +0x45, +0x4a, +0x5f, +0x53, +0x55, +0x4e +}; +static unsigned char ssdt_pcihp_start[] = { +0x30 +}; +static unsigned char ssdt_pcihp_id[] = { +0x3d +}; +static unsigned char ssdt_pcihp_ej0[] = { +0x4a +}; diff --git a/hw/i386/ssdt-proc.dsl b/hw/i386/ssdt-proc.dsl new file mode 100644 index 0000000000..8229bfd702 --- /dev/null +++ b/hw/i386/ssdt-proc.dsl @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* This file is the basis for the ssdt table generated in src/acpi.c. + * It defines the contents of the per-cpu Processor() object. At + * runtime, a dynamically generated SSDT will contain one copy of this + * AML snippet for every possible cpu in the system. The objects will + * be placed in the \_SB_ namespace. + * + * In addition to the aml code generated from this file, the + * src/acpi.c file creates a NTFY method with an entry for each cpu: + * Method(NTFY, 2) { + * If (LEqual(Arg0, 0x00)) { Notify(CP00, Arg1) } + * If (LEqual(Arg0, 0x01)) { Notify(CP01, Arg1) } + * ... + * } + * and a CPON array with the list of active and inactive cpus: + * Name(CPON, Package() { One, One, ..., Zero, Zero, ... }) + */ + +ACPI_EXTRACT_ALL_CODE ssdp_proc_aml + +DefinitionBlock ("ssdt-proc.aml", "SSDT", 0x01, "BXPC", "BXSSDT", 0x1) +{ + ACPI_EXTRACT_PROCESSOR_START ssdt_proc_start + ACPI_EXTRACT_PROCESSOR_END ssdt_proc_end + ACPI_EXTRACT_PROCESSOR_STRING ssdt_proc_name + Processor(CPAA, 0xAA, 0x00000000, 0x0) { + ACPI_EXTRACT_NAME_BYTE_CONST ssdt_proc_id + Name(ID, 0xAA) +/* + * The src/acpi.c code requires the above ACP_EXTRACT tags so that it can update + * CPAA and 0xAA with the appropriate CPU id (see + * SD_OFFSET_CPUHEX/CPUID1/CPUID2). Don't change the above without + * also updating the C code. + */ + Name(_HID, "ACPI0007") + External(CPMA, MethodObj) + External(CPST, MethodObj) + External(CPEJ, MethodObj) + Method(_MAT, 0) { + Return (CPMA(ID)) + } + Method(_STA, 0) { + Return (CPST(ID)) + } + Method(_EJ0, 1, NotSerialized) { + CPEJ(ID, Arg0) + } + } +} diff --git a/hw/i386/ssdt-proc.hex.generated b/hw/i386/ssdt-proc.hex.generated new file mode 100644 index 0000000000..bb9920d3c9 --- /dev/null +++ b/hw/i386/ssdt-proc.hex.generated @@ -0,0 +1,134 @@ +static unsigned char ssdt_proc_name[] = { +0x28 +}; +static unsigned char ssdp_proc_aml[] = { +0x53, +0x53, +0x44, +0x54, +0x78, +0x0, +0x0, +0x0, +0x1, +0xb8, +0x42, +0x58, +0x50, +0x43, +0x0, +0x0, +0x42, +0x58, +0x53, +0x53, +0x44, +0x54, +0x0, +0x0, +0x1, +0x0, +0x0, +0x0, +0x49, +0x4e, +0x54, +0x4c, +0x23, +0x8, +0x13, +0x20, +0x5b, +0x83, +0x42, +0x5, +0x43, +0x50, +0x41, +0x41, +0xaa, +0x10, +0xb0, +0x0, +0x0, +0x0, +0x8, +0x49, +0x44, +0x5f, +0x5f, +0xa, +0xaa, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xd, +0x41, +0x43, +0x50, +0x49, +0x30, +0x30, +0x30, +0x37, +0x0, +0x14, +0xf, +0x5f, +0x4d, +0x41, +0x54, +0x0, +0xa4, +0x43, +0x50, +0x4d, +0x41, +0x49, +0x44, +0x5f, +0x5f, +0x14, +0xf, +0x5f, +0x53, +0x54, +0x41, +0x0, +0xa4, +0x43, +0x50, +0x53, +0x54, +0x49, +0x44, +0x5f, +0x5f, +0x14, +0xf, +0x5f, +0x45, +0x4a, +0x30, +0x1, +0x43, +0x50, +0x45, +0x4a, +0x49, +0x44, +0x5f, +0x5f, +0x68 +}; +static unsigned char ssdt_proc_id[] = { +0x38 +}; +static unsigned char ssdt_proc_end[] = { +0x78 +}; +static unsigned char ssdt_proc_start[] = { +0x24 +}; diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index a8be62cf99..fbea9e8886 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -961,7 +961,8 @@ static int handle_cmd(AHCIState *s, int port, int slot) /* We're ready to process the command in FIS byte 2. */ ide_exec_cmd(&s->dev[port].port, cmd_fis[2]); - if (s->dev[port].port.ifs[0].status & READY_STAT) { + if ((s->dev[port].port.ifs[0].status & (READY_STAT|DRQ_STAT|BUSY_STAT)) == + READY_STAT) { ahci_write_fis_d2h(&s->dev[port], cmd_fis); } } diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 05e60b1cdc..f7d2009c00 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -437,7 +437,7 @@ static int ide_dvd_read_structure(IDEState *s, int format, cpu_to_ube32(buf + 16, total_sectors - 1); /* l0 end sector */ /* Size of buffer, not including 2 byte size field */ - cpu_to_be16wu((uint16_t *)buf, 2048 + 2); + stw_be_p(buf, 2048 + 2); /* 2k data + 4 byte header */ return (2048 + 4); @@ -448,7 +448,7 @@ static int ide_dvd_read_structure(IDEState *s, int format, buf[5] = 0; /* no region restrictions */ /* Size of buffer, not including 2 byte size field */ - cpu_to_be16wu((uint16_t *)buf, 4 + 2); + stw_be_p(buf, 4 + 2); /* 4 byte header + 4 byte data */ return (4 + 4); @@ -458,7 +458,7 @@ static int ide_dvd_read_structure(IDEState *s, int format, case 0x04: /* DVD disc manufacturing information */ /* Size of buffer, not including 2 byte size field */ - cpu_to_be16wu((uint16_t *)buf, 2048 + 2); + stw_be_p(buf, 2048 + 2); /* 2k data + 4 byte header */ return (2048 + 4); @@ -471,22 +471,22 @@ static int ide_dvd_read_structure(IDEState *s, int format, buf[4] = 0x00; /* Physical format */ buf[5] = 0x40; /* Not writable, is readable */ - cpu_to_be16wu((uint16_t *)(buf + 6), 2048 + 4); + stw_be_p(buf + 6, 2048 + 4); buf[8] = 0x01; /* Copyright info */ buf[9] = 0x40; /* Not writable, is readable */ - cpu_to_be16wu((uint16_t *)(buf + 10), 4 + 4); + stw_be_p(buf + 10, 4 + 4); buf[12] = 0x03; /* BCA info */ buf[13] = 0x40; /* Not writable, is readable */ - cpu_to_be16wu((uint16_t *)(buf + 14), 188 + 4); + stw_be_p(buf + 14, 188 + 4); buf[16] = 0x04; /* Manufacturing info */ buf[17] = 0x40; /* Not writable, is readable */ - cpu_to_be16wu((uint16_t *)(buf + 18), 2048 + 4); + stw_be_p(buf + 18, 2048 + 4); /* Size of buffer, not including 2 byte size field */ - cpu_to_be16wu((uint16_t *)buf, 16 + 2); + stw_be_p(buf, 16 + 2); /* data written + 4 byte header */ return (16 + 4); diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 0500a7a1b6..a8e35fe38f 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -230,7 +230,7 @@ static void cmd646_update_irq(PCIIDEState *d) !(pd->config[MRDMODE] & MRDMODE_BLK_CH0)) || ((pd->config[MRDMODE] & MRDMODE_INTR_CH1) && !(pd->config[MRDMODE] & MRDMODE_BLK_CH1)); - qemu_set_irq(pd->irq[0], pci_level); + pci_set_irq(pd, pci_level); } /* the PCI irq level is the logical OR of the two channels */ diff --git a/hw/ide/core.c b/hw/ide/core.c index 399b1bae68..e1f4c33fb8 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2215,55 +2215,6 @@ void ide_init2(IDEBus *bus, qemu_irq irq) bus->dma = &ide_dma_nop; } -/* TODO convert users to qdev and remove */ -void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0, - DriveInfo *hd1, qemu_irq irq) -{ - int i, trans; - DriveInfo *dinfo; - uint32_t cyls, heads, secs; - - for(i = 0; i < 2; i++) { - dinfo = i == 0 ? hd0 : hd1; - ide_init1(bus, i); - if (dinfo) { - cyls = dinfo->cyls; - heads = dinfo->heads; - secs = dinfo->secs; - trans = dinfo->trans; - if (!cyls && !heads && !secs) { - hd_geometry_guess(dinfo->bdrv, &cyls, &heads, &secs, &trans); - } else if (trans == BIOS_ATA_TRANSLATION_AUTO) { - trans = hd_bios_chs_auto_trans(cyls, heads, secs); - } - if (cyls < 1 || cyls > 65535) { - error_report("cyls must be between 1 and 65535"); - exit(1); - } - if (heads < 1 || heads > 16) { - error_report("heads must be between 1 and 16"); - exit(1); - } - if (secs < 1 || secs > 255) { - error_report("secs must be between 1 and 255"); - exit(1); - } - if (ide_init_drive(&bus->ifs[i], dinfo->bdrv, - dinfo->media_cd ? IDE_CD : IDE_HD, - NULL, dinfo->serial, NULL, 0, - cyls, heads, secs, trans) < 0) { - error_report("Can't set up IDE drive %s", dinfo->id); - exit(1); - } - bdrv_attach_dev_nofail(dinfo->bdrv, &bus->ifs[i]); - } else { - ide_reset(&bus->ifs[i]); - } - } - bus->irq = irq; - bus->dma = &ide_dma_nop; -} - static const MemoryRegionPortio ide_portio_list[] = { { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write }, { 0, 2, 2, .read = ide_data_readw, .write = ide_data_writew }, diff --git a/hw/ide/ich.c b/hw/ide/ich.c index bff952bf6a..1c7c05810d 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -116,7 +116,7 @@ static int pci_ich9_ahci_init(PCIDevice *dev) dev->config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */ msi_init(dev, 0x50, 1, true, false); - d->ahci.irq = dev->irq[0]; + d->ahci.irq = pci_allocate_irq(dev); pci_register_bar(dev, ICH9_IDP_BAR, PCI_BASE_ADDRESS_SPACE_IO, &d->ahci.idp); @@ -145,6 +145,7 @@ static void pci_ich9_uninit(PCIDevice *dev) msi_uninit(dev); ahci_uninit(&d->ahci); + qemu_free_irq(d->ahci.irq); } static void ich_ahci_class_init(ObjectClass *klass, void *data) diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 5d1cf87742..0567a522f5 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -553,8 +553,6 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, uint32_t cylinders, uint32_t heads, uint32_t secs, int chs_trans); void ide_init2(IDEBus *bus, qemu_irq irq); -void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0, - DriveInfo *hd1, qemu_irq irq); void ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); void ide_exec_cmd(IDEBus *bus, uint32_t val); diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index 92c1df0460..21d6495817 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -30,15 +30,22 @@ #include <hw/ide/internal.h> +#define TYPE_MICRODRIVE "microdrive" +#define MICRODRIVE(obj) OBJECT_CHECK(MicroDriveState, (obj), TYPE_MICRODRIVE) + /***********************************************************/ /* CF-ATA Microdrive */ #define METADATA_SIZE 0x20 /* DSCM-1XXXX Microdrive hard disk with CF+ II / PCMCIA interface. */ -typedef struct { + +typedef struct MicroDriveState { + /*< private >*/ + PCMCIACardState parent_obj; + /*< public >*/ + IDEBus bus; - PCMCIACardState card; uint32_t attr_base; uint32_t io_base; @@ -81,10 +88,13 @@ enum md_ctrl { static inline void md_interrupt_update(MicroDriveState *s) { - if (!s->card.slot) + PCMCIACardState *card = PCMCIA_CARD(s); + + if (card->slot == NULL) { return; + } - qemu_set_irq(s->card.slot->irq, + qemu_set_irq(card->slot->irq, !(s->stat & STAT_INT) && /* Inverted */ !(s->ctrl & (CTRL_IEN | CTRL_SRST)) && !(s->opt & OPT_SRESET)); @@ -93,16 +103,20 @@ static inline void md_interrupt_update(MicroDriveState *s) static void md_set_irq(void *opaque, int irq, int level) { MicroDriveState *s = opaque; - if (level) + + if (level) { s->stat |= STAT_INT; - else + } else { s->stat &= ~STAT_INT; + } md_interrupt_update(s); } -static void md_reset(MicroDriveState *s) +static void md_reset(DeviceState *dev) { + MicroDriveState *s = MICRODRIVE(dev); + s->opt = OPT_MODE_MMAP; s->stat = 0; s->pins = 0; @@ -111,14 +125,17 @@ static void md_reset(MicroDriveState *s) ide_bus_reset(&s->bus); } -static uint8_t md_attr_read(void *opaque, uint32_t at) +static uint8_t md_attr_read(PCMCIACardState *card, uint32_t at) { - MicroDriveState *s = opaque; + MicroDriveState *s = MICRODRIVE(card); + PCMCIACardClass *pcc = PCMCIA_CARD_GET_CLASS(card); + if (at < s->attr_base) { - if (at < s->card.cis_len) - return s->card.cis[at]; - else + if (at < pcc->cis_len) { + return pcc->cis[at]; + } else { return 0x00; + } } at -= s->attr_base; @@ -127,10 +144,11 @@ static uint8_t md_attr_read(void *opaque, uint32_t at) case 0x00: /* Configuration Option Register */ return s->opt; case 0x02: /* Card Configuration Status Register */ - if (s->ctrl & CTRL_IEN) + if (s->ctrl & CTRL_IEN) { return s->stat & ~STAT_INT; - else + } else { return s->stat; + } case 0x04: /* Pin Replacement Register */ return (s->pins & PINS_CRDY) | 0x0c; case 0x06: /* Socket and Copy Register */ @@ -144,21 +162,24 @@ static uint8_t md_attr_read(void *opaque, uint32_t at) return 0; } -static void md_attr_write(void *opaque, uint32_t at, uint8_t value) +static void md_attr_write(PCMCIACardState *card, uint32_t at, uint8_t value) { - MicroDriveState *s = opaque; + MicroDriveState *s = MICRODRIVE(card); + at -= s->attr_base; switch (at) { case 0x00: /* Configuration Option Register */ s->opt = value & 0xcf; - if (value & OPT_SRESET) - md_reset(s); + if (value & OPT_SRESET) { + device_reset(DEVICE(s)); + } md_interrupt_update(s); break; case 0x02: /* Card Configuration Status Register */ - if ((s->stat ^ value) & STAT_PWRDWN) + if ((s->stat ^ value) & STAT_PWRDWN) { s->pins |= PINS_CRDY; + } s->stat &= 0x82; s->stat |= value & 0x74; md_interrupt_update(s); @@ -175,32 +196,35 @@ static void md_attr_write(void *opaque, uint32_t at, uint8_t value) } } -static uint16_t md_common_read(void *opaque, uint32_t at) +static uint16_t md_common_read(PCMCIACardState *card, uint32_t at) { - MicroDriveState *s = opaque; + MicroDriveState *s = MICRODRIVE(card); IDEState *ifs; uint16_t ret; at -= s->io_base; switch (s->opt & OPT_MODE) { case OPT_MODE_MMAP: - if ((at & ~0x3ff) == 0x400) + if ((at & ~0x3ff) == 0x400) { at = 0; + } break; case OPT_MODE_IOMAP16: at &= 0xf; break; case OPT_MODE_IOMAP1: - if ((at & ~0xf) == 0x3f0) + if ((at & ~0xf) == 0x3f0) { at -= 0x3e8; - else if ((at & ~0xf) == 0x1f0) + } else if ((at & ~0xf) == 0x1f0) { at -= 0x1f0; + } break; case OPT_MODE_IOMAP2: - if ((at & ~0xf) == 0x370) + if ((at & ~0xf) == 0x370) { at -= 0x368; - else if ((at & ~0xf) == 0x170) + } else if ((at & ~0xf) == 0x170) { at -= 0x170; + } } switch (at) { @@ -209,9 +233,9 @@ static uint16_t md_common_read(void *opaque, uint32_t at) return ide_data_readw(&s->bus, 0); /* TODO: 8-bit accesses */ - if (s->cycle) + if (s->cycle) { ret = s->io >> 8; - else { + } else { s->io = ide_data_readw(&s->bus, 0); ret = s->io & 0xff; } @@ -223,10 +247,11 @@ static uint16_t md_common_read(void *opaque, uint32_t at) return ide_ioport_read(&s->bus, 0x1); case 0xe: /* Alternate Status */ ifs = idebus_active_if(&s->bus); - if (ifs->bs) + if (ifs->bs) { return ifs->status; - else + } else { return 0; + } case 0xf: /* Device Address */ ifs = idebus_active_if(&s->bus); return 0xc2 | ((~ifs->select << 2) & 0x3c); @@ -237,30 +262,33 @@ static uint16_t md_common_read(void *opaque, uint32_t at) return 0; } -static void md_common_write(void *opaque, uint32_t at, uint16_t value) +static void md_common_write(PCMCIACardState *card, uint32_t at, uint16_t value) { - MicroDriveState *s = opaque; + MicroDriveState *s = MICRODRIVE(card); at -= s->io_base; switch (s->opt & OPT_MODE) { case OPT_MODE_MMAP: - if ((at & ~0x3ff) == 0x400) + if ((at & ~0x3ff) == 0x400) { at = 0; + } break; case OPT_MODE_IOMAP16: at &= 0xf; break; case OPT_MODE_IOMAP1: - if ((at & ~0xf) == 0x3f0) + if ((at & ~0xf) == 0x3f0) { at -= 0x3e8; - else if ((at & ~0xf) == 0x1f0) + } else if ((at & ~0xf) == 0x1f0) { at -= 0x1f0; + } break; case OPT_MODE_IOMAP2: - if ((at & ~0xf) == 0x370) + if ((at & ~0xf) == 0x370) { at -= 0x368; - else if ((at & ~0xf) == 0x170) + } else if ((at & ~0xf) == 0x170) { at -= 0x170; + } } switch (at) { @@ -270,10 +298,11 @@ static void md_common_write(void *opaque, uint32_t at, uint16_t value) break; /* TODO: 8-bit accesses */ - if (s->cycle) + if (s->cycle) { ide_data_writew(&s->bus, 0, s->io | (value << 8)); - else + } else { s->io = value & 0xff; + } s->cycle = !s->cycle; break; case 0x9: @@ -285,8 +314,9 @@ static void md_common_write(void *opaque, uint32_t at, uint16_t value) break; case 0xe: /* Device Control */ s->ctrl = value; - if (value & CTRL_SRST) - md_reset(s); + if (value & CTRL_SRST) { + device_reset(DEVICE(s)); + } md_interrupt_update(s); break; default: @@ -501,49 +531,109 @@ static const uint8_t dscm1xxxx_cis[0x14a] = { [0x146] = CISTPL_END, /* Tuple End */ }; -static int dscm1xxxx_attach(void *opaque) +#define TYPE_DSCM1XXXX "dscm1xxxx" + +static int dscm1xxxx_attach(PCMCIACardState *card) { - MicroDriveState *md = opaque; - md->card.attr_read = md_attr_read; - md->card.attr_write = md_attr_write; - md->card.common_read = md_common_read; - md->card.common_write = md_common_write; - md->card.io_read = md_common_read; - md->card.io_write = md_common_write; - - md->attr_base = md->card.cis[0x74] | (md->card.cis[0x76] << 8); + MicroDriveState *md = MICRODRIVE(card); + PCMCIACardClass *pcc = PCMCIA_CARD_GET_CLASS(card); + + md->attr_base = pcc->cis[0x74] | (pcc->cis[0x76] << 8); md->io_base = 0x0; - md_reset(md); + device_reset(DEVICE(md)); md_interrupt_update(md); - md->card.slot->card_string = "DSCM-1xxxx Hitachi Microdrive"; + card->slot->card_string = "DSCM-1xxxx Hitachi Microdrive"; return 0; } -static int dscm1xxxx_detach(void *opaque) +static int dscm1xxxx_detach(PCMCIACardState *card) { - MicroDriveState *md = opaque; - md_reset(md); + MicroDriveState *md = MICRODRIVE(card); + + device_reset(DEVICE(md)); return 0; } -PCMCIACardState *dscm1xxxx_init(DriveInfo *bdrv) +PCMCIACardState *dscm1xxxx_init(DriveInfo *dinfo) { - MicroDriveState *md = (MicroDriveState *) g_malloc0(sizeof(MicroDriveState)); - md->card.state = md; - md->card.attach = dscm1xxxx_attach; - md->card.detach = dscm1xxxx_detach; - md->card.cis = dscm1xxxx_cis; - md->card.cis_len = sizeof(dscm1xxxx_cis); - - ide_init2_with_non_qdev_drives(&md->bus, bdrv, NULL, - qemu_allocate_irqs(md_set_irq, md, 1)[0]); + MicroDriveState *md; + + md = MICRODRIVE(object_new(TYPE_DSCM1XXXX)); + qdev_init_nofail(DEVICE(md)); + + if (dinfo != NULL) { + ide_create_drive(&md->bus, 0, dinfo); + } md->bus.ifs[0].drive_kind = IDE_CFATA; md->bus.ifs[0].mdata_size = METADATA_SIZE; md->bus.ifs[0].mdata_storage = (uint8_t *) g_malloc0(METADATA_SIZE); - vmstate_register(NULL, -1, &vmstate_microdrive, md); + return PCMCIA_CARD(md); +} + +static void dscm1xxxx_class_init(ObjectClass *oc, void *data) +{ + PCMCIACardClass *pcc = PCMCIA_CARD_CLASS(oc); + + pcc->cis = dscm1xxxx_cis; + pcc->cis_len = sizeof(dscm1xxxx_cis); + + pcc->attach = dscm1xxxx_attach; + pcc->detach = dscm1xxxx_detach; +} + +static const TypeInfo dscm1xxxx_type_info = { + .name = TYPE_DSCM1XXXX, + .parent = TYPE_MICRODRIVE, + .class_init = dscm1xxxx_class_init, +}; + +static void microdrive_realize(DeviceState *dev, Error **errp) +{ + MicroDriveState *md = MICRODRIVE(dev); + + ide_init2(&md->bus, qemu_allocate_irqs(md_set_irq, md, 1)[0]); +} + +static void microdrive_init(Object *obj) +{ + MicroDriveState *md = MICRODRIVE(obj); + + ide_bus_new(&md->bus, sizeof(md->bus), DEVICE(obj), 0, 1); +} - return &md->card; +static void microdrive_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCMCIACardClass *pcc = PCMCIA_CARD_CLASS(oc); + + pcc->attr_read = md_attr_read; + pcc->attr_write = md_attr_write; + pcc->common_read = md_common_read; + pcc->common_write = md_common_write; + pcc->io_read = md_common_read; + pcc->io_write = md_common_write; + + dc->realize = microdrive_realize; + dc->reset = md_reset; + dc->vmsd = &vmstate_microdrive; } + +static const TypeInfo microdrive_type_info = { + .name = TYPE_MICRODRIVE, + .parent = TYPE_PCMCIA_CARD, + .instance_size = sizeof(MicroDriveState), + .instance_init = microdrive_init, + .abstract = true, + .class_init = microdrive_class_init, +}; + +static void microdrive_register_types(void) +{ + type_register_static(µdrive_type_info); + type_register_static(&dscm1xxxx_type_info); +} + +type_init(microdrive_register_types) diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 2851eed25f..47ac44264c 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -23,3 +23,4 @@ obj-$(CONFIG_OMAP) += omap_intc.o obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o +obj-$(CONFIG_XICS_KVM) += xics_kvm.o diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 709b5c2984..c7658508dd 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -64,17 +64,17 @@ static const VMStateDescription vmstate_gic = { .post_load = gic_post_load, .fields = (VMStateField[]) { VMSTATE_BOOL(enabled, GICState), - VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, NCPU), + VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, GIC_NCPU), VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1, vmstate_gic_irq_state, gic_irq_state), VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ), - VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, NCPU), + VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU), VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL), - VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, NCPU), - VMSTATE_UINT16_ARRAY(priority_mask, GICState, NCPU), - VMSTATE_UINT16_ARRAY(running_irq, GICState, NCPU), - VMSTATE_UINT16_ARRAY(running_priority, GICState, NCPU), - VMSTATE_UINT16_ARRAY(current_pending, GICState, NCPU), + VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU), + VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU), + VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU), + VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU), + VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU), VMSTATE_END_OF_LIST() } }; @@ -84,9 +84,9 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp) GICState *s = ARM_GIC_COMMON(dev); int num_irq = s->num_irq; - if (s->num_cpu > NCPU) { + if (s->num_cpu > GIC_NCPU) { error_setg(errp, "requested %u CPUs exceeds GIC maximum %d", - s->num_cpu, NCPU); + s->num_cpu, GIC_NCPU); return; } s->num_irq += GIC_BASE_IRQ; diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 14264373fe..3989fd1bd5 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -21,16 +21,9 @@ #ifndef QEMU_ARM_GIC_INTERNAL_H #define QEMU_ARM_GIC_INTERNAL_H -#include "hw/sysbus.h" +#include "hw/intc/arm_gic.h" -/* Maximum number of possible interrupts, determined by the GIC architecture */ -#define GIC_MAXIRQ 1020 -/* First 32 are private to each CPU (SGIs and PPIs). */ -#define GIC_INTERNAL 32 -/* Maximum number of possible CPU interfaces, determined by GIC architecture */ -#define NCPU 8 - -#define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1))) +#define ALL_CPU_MASK ((unsigned)(((1 << GIC_NCPU) - 1))) /* The NVIC has 16 internal vectors. However these are not exposed through the normal GIC interface. */ @@ -59,48 +52,6 @@ s->priority2[(irq) - GIC_INTERNAL]) #define GIC_TARGET(irq) s->irq_target[irq] -typedef struct gic_irq_state { - /* The enable bits are only banked for per-cpu interrupts. */ - uint8_t enabled; - uint8_t pending; - uint8_t active; - uint8_t level; - bool model; /* 0 = N:N, 1 = 1:N */ - bool trigger; /* nonzero = edge triggered. */ -} gic_irq_state; - -typedef struct GICState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - qemu_irq parent_irq[NCPU]; - bool enabled; - bool cpu_enabled[NCPU]; - - gic_irq_state irq_state[GIC_MAXIRQ]; - uint8_t irq_target[GIC_MAXIRQ]; - uint8_t priority1[GIC_INTERNAL][NCPU]; - uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL]; - uint16_t last_active[GIC_MAXIRQ][NCPU]; - - uint16_t priority_mask[NCPU]; - uint16_t running_irq[NCPU]; - uint16_t running_priority[NCPU]; - uint16_t current_pending[NCPU]; - - uint32_t num_cpu; - - MemoryRegion iomem; /* Distributor */ - /* This is just so we can have an opaque pointer which identifies - * both this GIC and which CPU interface we should be accessing. - */ - struct GICState *backref[NCPU]; - MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */ - uint32_t num_irq; - uint32_t revision; -} GICState; - /* The special cases for the revision property: */ #define REV_11MPCORE 0 #define REV_NVIC 0xffffffff @@ -111,31 +62,4 @@ void gic_complete_irq(GICState *s, int cpu, int irq); void gic_update(GICState *s); void gic_init_irqs_and_distributor(GICState *s, int num_irq); -#define TYPE_ARM_GIC_COMMON "arm_gic_common" -#define ARM_GIC_COMMON(obj) \ - OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC_COMMON) -#define ARM_GIC_COMMON_CLASS(klass) \ - OBJECT_CLASS_CHECK(ARMGICCommonClass, (klass), TYPE_ARM_GIC_COMMON) -#define ARM_GIC_COMMON_GET_CLASS(obj) \ - OBJECT_GET_CLASS(ARMGICCommonClass, (obj), TYPE_ARM_GIC_COMMON) - -typedef struct ARMGICCommonClass { - SysBusDeviceClass parent_class; - void (*pre_save)(GICState *s); - void (*post_load)(GICState *s); -} ARMGICCommonClass; - -#define TYPE_ARM_GIC "arm_gic" -#define ARM_GIC(obj) \ - OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC) -#define ARM_GIC_CLASS(klass) \ - OBJECT_CLASS_CHECK(ARMGICClass, (klass), TYPE_ARM_GIC) -#define ARM_GIC_GET_CLASS(obj) \ - OBJECT_GET_CLASS(ARMGICClass, (obj), TYPE_ARM_GIC) - -typedef struct ARMGICClass { - ARMGICCommonClass parent_class; - DeviceRealize parent_realize; -} ARMGICClass; - #endif /* !QEMU_ARM_GIC_INTERNAL_H */ diff --git a/hw/intc/realview_gic.c b/hw/intc/realview_gic.c index ce8044780a..6c812961cc 100644 --- a/hw/intc/realview_gic.c +++ b/hw/intc/realview_gic.c @@ -7,41 +7,34 @@ * This code is licensed under the GPL. */ -#include "hw/sysbus.h" - -#define TYPE_REALVIEW_GIC "realview_gic" -#define REALVIEW_GIC(obj) \ - OBJECT_CHECK(RealViewGICState, (obj), TYPE_REALVIEW_GIC) - -typedef struct { - SysBusDevice parent_obj; - - DeviceState *gic; - MemoryRegion container; -} RealViewGICState; +#include "hw/intc/realview_gic.h" static void realview_gic_set_irq(void *opaque, int irq, int level) { RealViewGICState *s = (RealViewGICState *)opaque; - qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); + + qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level); } -static int realview_gic_init(SysBusDevice *sbd) +static void realview_gic_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); RealViewGICState *s = REALVIEW_GIC(dev); SysBusDevice *busdev; + Error *err = NULL; /* The GICs on the RealView boards have a fixed nonconfigurable * number of interrupt lines, so we don't need to expose this as * a qdev property. */ int numirq = 96; - s->gic = qdev_create(NULL, "arm_gic"); - qdev_prop_set_uint32(s->gic, "num-cpu", 1); - qdev_prop_set_uint32(s->gic, "num-irq", numirq); - qdev_init_nofail(s->gic); - busdev = SYS_BUS_DEVICE(s->gic); + qdev_prop_set_uint32(DEVICE(&s->gic), "num-irq", numirq); + object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + busdev = SYS_BUS_DEVICE(&s->gic); /* Pass through outbound IRQ lines from the GIC */ sysbus_pass_irq(sbd, busdev); @@ -49,27 +42,40 @@ static int realview_gic_init(SysBusDevice *sbd) /* Pass through inbound GPIO lines to the GIC */ qdev_init_gpio_in(dev, realview_gic_set_irq, numirq - 32); - memory_region_init(&s->container, OBJECT(s), - "realview-gic-container", 0x2000); memory_region_add_subregion(&s->container, 0, sysbus_mmio_get_region(busdev, 1)); memory_region_add_subregion(&s->container, 0x1000, sysbus_mmio_get_region(busdev, 0)); +} + +static void realview_gic_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RealViewGICState *s = REALVIEW_GIC(obj); + DeviceState *gicdev; + + memory_region_init(&s->container, OBJECT(s), + "realview-gic-container", 0x2000); sysbus_init_mmio(sbd, &s->container); - return 0; + + object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); + gicdev = DEVICE(&s->gic); + qdev_set_parent_bus(gicdev, sysbus_get_default()); + qdev_prop_set_uint32(gicdev, "num-cpu", 1); } -static void realview_gic_class_init(ObjectClass *klass, void *data) +static void realview_gic_class_init(ObjectClass *oc, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(oc); - sdc->init = realview_gic_init; + dc->realize = realview_gic_realize; } static const TypeInfo realview_gic_info = { .name = TYPE_REALVIEW_GIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(RealViewGICState), + .instance_init = realview_gic_init, .class_init = realview_gic_class_init, }; diff --git a/hw/intc/xics.c b/hw/intc/xics.c index bb018d1829..a333305d3d 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -27,8 +27,148 @@ #include "hw/hw.h" #include "trace.h" +#include "qemu/timer.h" #include "hw/ppc/spapr.h" #include "hw/ppc/xics.h" +#include "qemu/error-report.h" +#include "qapi/visitor.h" + +void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + ICPState *ss = &icp->ss[cs->cpu_index]; + XICSStateClass *info = XICS_COMMON_GET_CLASS(icp); + + assert(cs->cpu_index < icp->nr_servers); + + if (info->cpu_setup) { + info->cpu_setup(icp, cpu); + } + + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_POWER7: + ss->output = env->irq_inputs[POWER7_INPUT_INT]; + break; + + case PPC_FLAGS_INPUT_970: + ss->output = env->irq_inputs[PPC970_INPUT_INT]; + break; + + default: + error_report("XICS interrupt controller does not support this CPU " + "bus model"); + abort(); + } +} + +/* + * XICS Common class - parent for emulated XICS and KVM-XICS + */ +static void xics_common_reset(DeviceState *d) +{ + XICSState *icp = XICS_COMMON(d); + int i; + + for (i = 0; i < icp->nr_servers; i++) { + device_reset(DEVICE(&icp->ss[i])); + } + + device_reset(DEVICE(icp->ics)); +} + +static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, + void *opaque, const char *name, Error **errp) +{ + XICSState *icp = XICS_COMMON(obj); + int64_t value = icp->nr_irqs; + + visit_type_int(v, &value, name, errp); +} + +static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, + void *opaque, const char *name, Error **errp) +{ + XICSState *icp = XICS_COMMON(obj); + XICSStateClass *info = XICS_COMMON_GET_CLASS(icp); + Error *error = NULL; + int64_t value; + + visit_type_int(v, &value, name, &error); + if (error) { + error_propagate(errp, error); + return; + } + if (icp->nr_irqs) { + error_setg(errp, "Number of interrupts is already set to %u", + icp->nr_irqs); + return; + } + + assert(info->set_nr_irqs); + assert(icp->ics); + info->set_nr_irqs(icp, value, errp); +} + +static void xics_prop_get_nr_servers(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + XICSState *icp = XICS_COMMON(obj); + int64_t value = icp->nr_servers; + + visit_type_int(v, &value, name, errp); +} + +static void xics_prop_set_nr_servers(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + XICSState *icp = XICS_COMMON(obj); + XICSStateClass *info = XICS_COMMON_GET_CLASS(icp); + Error *error = NULL; + int64_t value; + + visit_type_int(v, &value, name, &error); + if (error) { + error_propagate(errp, error); + return; + } + if (icp->nr_servers) { + error_setg(errp, "Number of servers is already set to %u", + icp->nr_servers); + return; + } + + assert(info->set_nr_servers); + info->set_nr_servers(icp, value, errp); +} + +static void xics_common_initfn(Object *obj) +{ + object_property_add(obj, "nr_irqs", "int", + xics_prop_get_nr_irqs, xics_prop_set_nr_irqs, + NULL, NULL, NULL); + object_property_add(obj, "nr_servers", "int", + xics_prop_get_nr_servers, xics_prop_set_nr_servers, + NULL, NULL, NULL); +} + +static void xics_common_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->reset = xics_common_reset; +} + +static const TypeInfo xics_common_info = { + .name = TYPE_XICS_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XICSState), + .class_size = sizeof(XICSStateClass), + .instance_init = xics_common_initfn, + .class_init = xics_common_class_init, +}; /* * ICP: Presentation layer @@ -153,11 +293,35 @@ static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority) } } +static void icp_dispatch_pre_save(void *opaque) +{ + ICPState *ss = opaque; + ICPStateClass *info = ICP_GET_CLASS(ss); + + if (info->pre_save) { + info->pre_save(ss); + } +} + +static int icp_dispatch_post_load(void *opaque, int version_id) +{ + ICPState *ss = opaque; + ICPStateClass *info = ICP_GET_CLASS(ss); + + if (info->post_load) { + return info->post_load(ss, version_id); + } + + return 0; +} + static const VMStateDescription vmstate_icp_server = { .name = "icp/server", .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, + .pre_save = icp_dispatch_pre_save, + .post_load = icp_dispatch_post_load, .fields = (VMStateField []) { /* Sanity check */ VMSTATE_UINT32(xirr, ICPState), @@ -187,11 +351,12 @@ static void icp_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_icp_server; } -static TypeInfo icp_info = { +static const TypeInfo icp_info = { .name = TYPE_ICP, .parent = TYPE_DEVICE, .instance_size = sizeof(ICPState), .class_init = icp_class_init, + .class_size = sizeof(ICPStateClass), }; /* @@ -353,10 +518,9 @@ static void ics_reset(DeviceState *dev) } } -static int ics_post_load(void *opaque, int version_id) +static int ics_post_load(ICSState *ics, int version_id) { int i; - ICSState *ics = opaque; for (i = 0; i < ics->icp->nr_servers; i++) { icp_resend(ics->icp, i); @@ -365,6 +529,28 @@ static int ics_post_load(void *opaque, int version_id) return 0; } +static void ics_dispatch_pre_save(void *opaque) +{ + ICSState *ics = opaque; + ICSStateClass *info = ICS_GET_CLASS(ics); + + if (info->pre_save) { + info->pre_save(ics); + } +} + +static int ics_dispatch_post_load(void *opaque, int version_id) +{ + ICSState *ics = opaque; + ICSStateClass *info = ICS_GET_CLASS(ics); + + if (info->post_load) { + return info->post_load(ics, version_id); + } + + return 0; +} + static const VMStateDescription vmstate_ics_irq = { .name = "ics/irq", .version_id = 1, @@ -384,7 +570,8 @@ static const VMStateDescription vmstate_ics = { .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, - .post_load = ics_post_load, + .pre_save = ics_dispatch_pre_save, + .post_load = ics_dispatch_post_load, .fields = (VMStateField []) { /* Sanity check */ VMSTATE_UINT32_EQUAL(nr_irqs, ICSState), @@ -395,31 +582,44 @@ static const VMStateDescription vmstate_ics = { }, }; -static int ics_realize(DeviceState *dev) +static void ics_initfn(Object *obj) +{ + ICSState *ics = ICS(obj); + + ics->offset = XICS_IRQ_BASE; +} + +static void ics_realize(DeviceState *dev, Error **errp) { ICSState *ics = ICS(dev); + if (!ics->nr_irqs) { + error_setg(errp, "Number of interrupts needs to be greater 0"); + return; + } ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool)); ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs); - - return 0; } static void ics_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ICSStateClass *isc = ICS_CLASS(klass); - dc->init = ics_realize; + dc->realize = ics_realize; dc->vmsd = &vmstate_ics; dc->reset = ics_reset; + isc->post_load = ics_post_load; } -static TypeInfo ics_info = { +static const TypeInfo ics_info = { .name = TYPE_ICS, .parent = TYPE_DEVICE, .instance_size = sizeof(ICSState), .class_init = ics_class_init, + .class_size = sizeof(ICSStateClass), + .instance_init = ics_initfn, }; /* @@ -480,6 +680,18 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr, return H_SUCCESS; } +static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUState *cs = CPU(cpu); + ICPState *ss = &spapr->icp->ss[cs->cpu_index]; + uint32_t xirr = icp_accept(ss); + + args[0] = xirr; + args[1] = cpu_get_real_ticks(); + return H_SUCCESS; +} + static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { @@ -490,6 +702,18 @@ static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr, return H_SUCCESS; } +static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUState *cs = CPU(cpu); + ICPState *ss = &spapr->icp->ss[cs->cpu_index]; + + args[0] = ss->xirr; + args[1] = ss->mfrr; + + return H_SUCCESS; +} + static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t token, uint32_t nargs, target_ulong args, @@ -600,48 +824,39 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, * XICS */ -static void xics_reset(DeviceState *d) +static void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp) { - XICSState *icp = XICS(d); - int i; - - for (i = 0; i < icp->nr_servers; i++) { - device_reset(DEVICE(&icp->ss[i])); - } - - device_reset(DEVICE(icp->ics)); + icp->nr_irqs = icp->ics->nr_irqs = nr_irqs; } -void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) +static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, + Error **errp) { - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - ICPState *ss = &icp->ss[cs->cpu_index]; - - assert(cs->cpu_index < icp->nr_servers); - - switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_POWER7: - ss->output = env->irq_inputs[POWER7_INPUT_INT]; - break; + int i; - case PPC_FLAGS_INPUT_970: - ss->output = env->irq_inputs[PPC970_INPUT_INT]; - break; + icp->nr_servers = nr_servers; - default: - fprintf(stderr, "XICS interrupt controller does not support this CPU " - "bus model\n"); - abort(); + icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); + for (i = 0; i < icp->nr_servers; i++) { + char buffer[32]; + object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP); + snprintf(buffer, sizeof(buffer), "icp[%d]", i); + object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), + errp); } } static void xics_realize(DeviceState *dev, Error **errp) { XICSState *icp = XICS(dev); - ICSState *ics = icp->ics; + Error *error = NULL; int i; + if (!icp->nr_servers) { + error_setg(errp, "Number of servers needs to be greater 0"); + return; + } + /* Registration of global state belongs into realize */ spapr_rtas_register("ibm,set-xive", rtas_set_xive); spapr_rtas_register("ibm,get-xive", rtas_get_xive); @@ -651,20 +866,22 @@ static void xics_realize(DeviceState *dev, Error **errp) spapr_register_hypercall(H_CPPR, h_cppr); spapr_register_hypercall(H_IPI, h_ipi); spapr_register_hypercall(H_XIRR, h_xirr); + spapr_register_hypercall(H_XIRR_X, h_xirr_x); spapr_register_hypercall(H_EOI, h_eoi); + spapr_register_hypercall(H_IPOLL, h_ipoll); - ics->nr_irqs = icp->nr_irqs; - ics->offset = XICS_IRQ_BASE; - ics->icp = icp; - qdev_init_nofail(DEVICE(ics)); + object_property_set_bool(OBJECT(icp->ics), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } - icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); for (i = 0; i < icp->nr_servers; i++) { - char buffer[32]; - object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP); - snprintf(buffer, sizeof(buffer), "icp[%d]", i); - object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), NULL); - qdev_init_nofail(DEVICE(&icp->ss[i])); + object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } } } @@ -674,33 +891,31 @@ static void xics_initfn(Object *obj) xics->ics = ICS(object_new(TYPE_ICS)); object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); + xics->ics->icp = xics; } -static Property xics_properties[] = { - DEFINE_PROP_UINT32("nr_servers", XICSState, nr_servers, -1), - DEFINE_PROP_UINT32("nr_irqs", XICSState, nr_irqs, -1), - DEFINE_PROP_END_OF_LIST(), -}; - static void xics_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + XICSStateClass *xsc = XICS_CLASS(oc); dc->realize = xics_realize; - dc->props = xics_properties; - dc->reset = xics_reset; + xsc->set_nr_irqs = xics_set_nr_irqs; + xsc->set_nr_servers = xics_set_nr_servers; } static const TypeInfo xics_info = { .name = TYPE_XICS, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_XICS_COMMON, .instance_size = sizeof(XICSState), + .class_size = sizeof(XICSStateClass), .class_init = xics_class_init, .instance_init = xics_initfn, }; static void xics_register_types(void) { + type_register_static(&xics_common_info); type_register_static(&xics_info); type_register_static(&ics_info); type_register_static(&icp_info); diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c new file mode 100644 index 0000000000..c203646bd6 --- /dev/null +++ b/hw/intc/xics_kvm.c @@ -0,0 +1,494 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics, in-kernel emulation + * + * Copyright (c) 2013 David Gibson, IBM Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "hw/hw.h" +#include "trace.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/xics.h" +#include "kvm_ppc.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" + +#include <sys/ioctl.h> + +typedef struct KVMXICSState { + XICSState parent_obj; + + uint32_t set_xive_token; + uint32_t get_xive_token; + uint32_t int_off_token; + uint32_t int_on_token; + int kernel_xics_fd; +} KVMXICSState; + +/* + * ICP-KVM + */ +static void icp_get_kvm_state(ICPState *ss) +{ + uint64_t state; + struct kvm_one_reg reg = { + .id = KVM_REG_PPC_ICP_STATE, + .addr = (uintptr_t)&state, + }; + int ret; + + /* ICP for this CPU thread is not in use, exiting */ + if (!ss->cs) { + return; + } + + ret = kvm_vcpu_ioctl(ss->cs, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to retrieve KVM interrupt controller state" + " for CPU %d: %s", ss->cs->cpu_index, strerror(errno)); + exit(1); + } + + ss->xirr = state >> KVM_REG_PPC_ICP_XISR_SHIFT; + ss->mfrr = (state >> KVM_REG_PPC_ICP_MFRR_SHIFT) + & KVM_REG_PPC_ICP_MFRR_MASK; + ss->pending_priority = (state >> KVM_REG_PPC_ICP_PPRI_SHIFT) + & KVM_REG_PPC_ICP_PPRI_MASK; +} + +static int icp_set_kvm_state(ICPState *ss, int version_id) +{ + uint64_t state; + struct kvm_one_reg reg = { + .id = KVM_REG_PPC_ICP_STATE, + .addr = (uintptr_t)&state, + }; + int ret; + + /* ICP for this CPU thread is not in use, exiting */ + if (!ss->cs) { + return 0; + } + + state = ((uint64_t)ss->xirr << KVM_REG_PPC_ICP_XISR_SHIFT) + | ((uint64_t)ss->mfrr << KVM_REG_PPC_ICP_MFRR_SHIFT) + | ((uint64_t)ss->pending_priority << KVM_REG_PPC_ICP_PPRI_SHIFT); + + ret = kvm_vcpu_ioctl(ss->cs, KVM_SET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to restore KVM interrupt controller state (0x%" + PRIx64 ") for CPU %d: %s", state, ss->cs->cpu_index, + strerror(errno)); + return ret; + } + + return 0; +} + +static void icp_kvm_reset(DeviceState *dev) +{ + ICPState *icp = ICP(dev); + + icp->xirr = 0; + icp->pending_priority = 0xff; + icp->mfrr = 0xff; + + /* Make all outputs are deasserted */ + qemu_set_irq(icp->output, 0); + + icp_set_kvm_state(icp, 1); +} + +static void icp_kvm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICPStateClass *icpc = ICP_CLASS(klass); + + dc->reset = icp_kvm_reset; + icpc->pre_save = icp_get_kvm_state; + icpc->post_load = icp_set_kvm_state; +} + +static const TypeInfo icp_kvm_info = { + .name = TYPE_KVM_ICP, + .parent = TYPE_ICP, + .instance_size = sizeof(ICPState), + .class_init = icp_kvm_class_init, + .class_size = sizeof(ICPStateClass), +}; + +/* + * ICS-KVM + */ +static void ics_get_kvm_state(ICSState *ics) +{ + KVMXICSState *icpkvm = KVM_XICS(ics->icp); + uint64_t state; + struct kvm_device_attr attr = { + .flags = 0, + .group = KVM_DEV_XICS_GRP_SOURCES, + .addr = (uint64_t)(uintptr_t)&state, + }; + int i; + + for (i = 0; i < ics->nr_irqs; i++) { + ICSIRQState *irq = &ics->irqs[i]; + int ret; + + attr.attr = i + ics->offset; + + ret = ioctl(icpkvm->kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr); + if (ret != 0) { + error_report("Unable to retrieve KVM interrupt controller state" + " for IRQ %d: %s", i + ics->offset, strerror(errno)); + exit(1); + } + + irq->server = state & KVM_XICS_DESTINATION_MASK; + irq->saved_priority = (state >> KVM_XICS_PRIORITY_SHIFT) + & KVM_XICS_PRIORITY_MASK; + /* + * To be consistent with the software emulation in xics.c, we + * split out the masked state + priority that we get from the + * kernel into 'current priority' (0xff if masked) and + * 'saved priority' (if masked, this is the priority the + * interrupt had before it was masked). Masking and unmasking + * are done with the ibm,int-off and ibm,int-on RTAS calls. + */ + if (state & KVM_XICS_MASKED) { + irq->priority = 0xff; + } else { + irq->priority = irq->saved_priority; + } + + if (state & KVM_XICS_PENDING) { + if (state & KVM_XICS_LEVEL_SENSITIVE) { + irq->status |= XICS_STATUS_ASSERTED; + } else { + /* + * A pending edge-triggered interrupt (or MSI) + * must have been rejected previously when we + * first detected it and tried to deliver it, + * so mark it as pending and previously rejected + * for consistency with how xics.c works. + */ + irq->status |= XICS_STATUS_MASKED_PENDING + | XICS_STATUS_REJECTED; + } + } + } +} + +static int ics_set_kvm_state(ICSState *ics, int version_id) +{ + KVMXICSState *icpkvm = KVM_XICS(ics->icp); + uint64_t state; + struct kvm_device_attr attr = { + .flags = 0, + .group = KVM_DEV_XICS_GRP_SOURCES, + .addr = (uint64_t)(uintptr_t)&state, + }; + int i; + + for (i = 0; i < ics->nr_irqs; i++) { + ICSIRQState *irq = &ics->irqs[i]; + int ret; + + attr.attr = i + ics->offset; + + state = irq->server; + state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK) + << KVM_XICS_PRIORITY_SHIFT; + if (irq->priority != irq->saved_priority) { + assert(irq->priority == 0xff); + state |= KVM_XICS_MASKED; + } + + if (ics->islsi[i]) { + state |= KVM_XICS_LEVEL_SENSITIVE; + if (irq->status & XICS_STATUS_ASSERTED) { + state |= KVM_XICS_PENDING; + } + } else { + if (irq->status & XICS_STATUS_MASKED_PENDING) { + state |= KVM_XICS_PENDING; + } + } + + ret = ioctl(icpkvm->kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr); + if (ret != 0) { + error_report("Unable to restore KVM interrupt controller state" + " for IRQs %d: %s", i + ics->offset, strerror(errno)); + return ret; + } + } + + return 0; +} + +static void ics_kvm_set_irq(void *opaque, int srcno, int val) +{ + ICSState *ics = opaque; + struct kvm_irq_level args; + int rc; + + args.irq = srcno + ics->offset; + if (!ics->islsi[srcno]) { + if (!val) { + return; + } + args.level = KVM_INTERRUPT_SET; + } else { + args.level = val ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET; + } + rc = kvm_vm_ioctl(kvm_state, KVM_IRQ_LINE, &args); + if (rc < 0) { + perror("kvm_irq_line"); + } +} + +static void ics_kvm_reset(DeviceState *dev) +{ + ics_set_kvm_state(ICS(dev), 1); +} + +static void ics_kvm_realize(DeviceState *dev, Error **errp) +{ + ICSState *ics = ICS(dev); + + if (!ics->nr_irqs) { + error_setg(errp, "Number of interrupts needs to be greater 0"); + return; + } + ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); + ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool)); + ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs); +} + +static void ics_kvm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICSStateClass *icsc = ICS_CLASS(klass); + + dc->realize = ics_kvm_realize; + dc->reset = ics_kvm_reset; + icsc->pre_save = ics_get_kvm_state; + icsc->post_load = ics_set_kvm_state; +} + +static const TypeInfo ics_kvm_info = { + .name = TYPE_KVM_ICS, + .parent = TYPE_ICS, + .instance_size = sizeof(ICSState), + .class_init = ics_kvm_class_init, +}; + +/* + * XICS-KVM + */ +static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu) +{ + CPUState *cs; + ICPState *ss; + KVMXICSState *icpkvm = KVM_XICS(icp); + + cs = CPU(cpu); + ss = &icp->ss[cs->cpu_index]; + + assert(cs->cpu_index < icp->nr_servers); + if (icpkvm->kernel_xics_fd == -1) { + abort(); + } + + if (icpkvm->kernel_xics_fd != -1) { + int ret; + struct kvm_enable_cap xics_enable_cap = { + .cap = KVM_CAP_IRQ_XICS, + .flags = 0, + .args = {icpkvm->kernel_xics_fd, cs->cpu_index, 0, 0}, + }; + + ss->cs = cs; + + ret = kvm_vcpu_ioctl(ss->cs, KVM_ENABLE_CAP, &xics_enable_cap); + if (ret < 0) { + error_report("Unable to connect CPU%d to kernel XICS: %s", + cs->cpu_index, strerror(errno)); + exit(1); + } + } +} + +static void xics_kvm_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp) +{ + icp->nr_irqs = icp->ics->nr_irqs = nr_irqs; +} + +static void xics_kvm_set_nr_servers(XICSState *icp, uint32_t nr_servers, + Error **errp) +{ + int i; + + icp->nr_servers = nr_servers; + + icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); + for (i = 0; i < icp->nr_servers; i++) { + char buffer[32]; + object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_KVM_ICP); + snprintf(buffer, sizeof(buffer), "icp[%d]", i); + object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), + errp); + } +} + +static void rtas_dummy(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + error_report("pseries: %s must never be called for in-kernel XICS", + __func__); +} + +static void xics_kvm_realize(DeviceState *dev, Error **errp) +{ + KVMXICSState *icpkvm = KVM_XICS(dev); + XICSState *icp = XICS_COMMON(dev); + int i, rc; + Error *error = NULL; + struct kvm_create_device xics_create_device = { + .type = KVM_DEV_TYPE_XICS, + .flags = 0, + }; + + if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) { + error_setg(errp, + "KVM and IRQ_XICS capability must be present for in-kernel XICS"); + goto fail; + } + + icpkvm->set_xive_token = spapr_rtas_register("ibm,set-xive", rtas_dummy); + icpkvm->get_xive_token = spapr_rtas_register("ibm,get-xive", rtas_dummy); + icpkvm->int_off_token = spapr_rtas_register("ibm,int-off", rtas_dummy); + icpkvm->int_on_token = spapr_rtas_register("ibm,int-on", rtas_dummy); + + rc = kvmppc_define_rtas_kernel_token(icpkvm->set_xive_token, + "ibm,set-xive"); + if (rc < 0) { + error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,set-xive"); + goto fail; + } + + rc = kvmppc_define_rtas_kernel_token(icpkvm->get_xive_token, + "ibm,get-xive"); + if (rc < 0) { + error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,get-xive"); + goto fail; + } + + rc = kvmppc_define_rtas_kernel_token(icpkvm->int_on_token, "ibm,int-on"); + if (rc < 0) { + error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-on"); + goto fail; + } + + rc = kvmppc_define_rtas_kernel_token(icpkvm->int_off_token, "ibm,int-off"); + if (rc < 0) { + error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-off"); + goto fail; + } + + /* Create the kernel ICP */ + rc = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &xics_create_device); + if (rc < 0) { + error_setg_errno(errp, -rc, "Error on KVM_CREATE_DEVICE for XICS"); + goto fail; + } + + icpkvm->kernel_xics_fd = xics_create_device.fd; + + object_property_set_bool(OBJECT(icp->ics), true, "realized", &error); + if (error) { + error_propagate(errp, error); + goto fail; + } + + assert(icp->nr_servers); + for (i = 0; i < icp->nr_servers; i++) { + object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error); + if (error) { + error_propagate(errp, error); + goto fail; + } + } + + kvm_kernel_irqchip = true; + kvm_irqfds_allowed = true; + kvm_msi_via_irqfd_allowed = true; + kvm_gsi_direct_mapping = true; + + return; + +fail: + kvmppc_define_rtas_kernel_token(0, "ibm,set-xive"); + kvmppc_define_rtas_kernel_token(0, "ibm,get-xive"); + kvmppc_define_rtas_kernel_token(0, "ibm,int-on"); + kvmppc_define_rtas_kernel_token(0, "ibm,int-off"); +} + +static void xics_kvm_initfn(Object *obj) +{ + XICSState *xics = XICS_COMMON(obj); + + xics->ics = ICS(object_new(TYPE_KVM_ICS)); + object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); + xics->ics->icp = xics; +} + +static void xics_kvm_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + XICSStateClass *xsc = XICS_COMMON_CLASS(oc); + + dc->realize = xics_kvm_realize; + xsc->cpu_setup = xics_kvm_cpu_setup; + xsc->set_nr_irqs = xics_kvm_set_nr_irqs; + xsc->set_nr_servers = xics_kvm_set_nr_servers; +} + +static const TypeInfo xics_kvm_info = { + .name = TYPE_KVM_XICS, + .parent = TYPE_XICS_COMMON, + .instance_size = sizeof(KVMXICSState), + .class_init = xics_kvm_class_init, + .instance_init = xics_kvm_initfn, +}; + +static void xics_kvm_register_types(void) +{ + type_register_static(&xics_kvm_info); + type_register_static(&ics_kvm_info); + type_register_static(&icp_kvm_info); +} + +type_init(xics_kvm_register_types) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 5633d08b62..19b2198fa6 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -29,6 +29,7 @@ */ #include "qemu-common.h" #include "hw/hw.h" +#include "qapi/visitor.h" #include "qemu/range.h" #include "hw/isa/isa.h" #include "hw/sysbus.h" @@ -525,6 +526,43 @@ static const MemoryRegionOps ich9_rst_cnt_ops = { .endianness = DEVICE_LITTLE_ENDIAN }; +Object *ich9_lpc_find(void) +{ + bool ambig; + Object *o = object_resolve_path_type("", TYPE_ICH9_LPC_DEVICE, &ambig); + + if (ambig) { + return NULL; + } + return o; +} + +static void ich9_lpc_get_sci_int(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + ICH9LPCState *lpc = ICH9_LPC_DEVICE(obj); + uint32_t value = ich9_lpc_sci_irq(lpc); + + visit_type_uint32(v, &value, name, errp); +} + +static void ich9_lpc_add_properties(ICH9LPCState *lpc) +{ + static const uint8_t acpi_enable_cmd = ICH9_APM_ACPI_ENABLE; + static const uint8_t acpi_disable_cmd = ICH9_APM_ACPI_DISABLE; + + object_property_add(OBJECT(lpc), ACPI_PM_PROP_SCI_INT, "uint32", + ich9_lpc_get_sci_int, + NULL, NULL, NULL, NULL); + object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_ENABLE_CMD, + &acpi_enable_cmd, NULL); + object_property_add_uint8_ptr(OBJECT(lpc), ACPI_PM_PROP_ACPI_DISABLE_CMD, + &acpi_disable_cmd, NULL); + + ich9_pm_add_properties(OBJECT(lpc), &lpc->pm, NULL); +} + static int ich9_lpc_initfn(PCIDevice *d) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(d); @@ -552,6 +590,8 @@ static int ich9_lpc_initfn(PCIDevice *d) ICH9_RST_CNT_IOPORT, &lpc->rst_cnt_mem, 1); + ich9_lpc_add_properties(lpc); + return 0; } diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 8fe4fcb4a1..5fb808630f 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -185,7 +185,7 @@ static void pm_update_sci(VT686PMState *s) ACPI_BITMASK_POWER_BUTTON_ENABLE | ACPI_BITMASK_GLOBAL_LOCK_ENABLE | ACPI_BITMASK_TIMER_ENABLE)) != 0); - qemu_set_irq(s->dev.irq[0], sci_level); + pci_set_irq(&s->dev, sci_level); /* schedule a timer interruption if needed */ acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) && !(pmsts & ACPI_BITMASK_TIMER_STATUS)); diff --git a/hw/lm32/lm32_hwsetup.h b/hw/lm32/lm32_hwsetup.h index 3449bd8dfc..9fd5e697a7 100644 --- a/hw/lm32/lm32_hwsetup.h +++ b/hw/lm32/lm32_hwsetup.h @@ -73,7 +73,7 @@ static inline void hwsetup_free(HWSetup *hw) static inline void hwsetup_create_rom(HWSetup *hw, hwaddr base) { - rom_add_blob("hwsetup", hw->data, TARGET_PAGE_SIZE, base); + rom_add_blob("hwsetup", hw->data, TARGET_PAGE_SIZE, base, NULL, NULL, NULL); } static inline void hwsetup_add_u8(HWSetup *hw, uint8_t u) diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index f1744ec07e..15053c4c37 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -21,6 +21,7 @@ #include "hw/hw.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" +#include "sysemu/qtest.h" #include "hw/devices.h" #include "hw/boards.h" #include "hw/loader.h" @@ -143,7 +144,7 @@ milkymist_init(QEMUMachineInitArgs *args) reset_info->bootstrap_pc = BIOS_OFFSET; /* if no kernel is given no valid bios rom is a fatal error */ - if (!kernel_filename && !dinfo && !bios_filename) { + if (!kernel_filename && !dinfo && !bios_filename && !qtest_enabled()) { fprintf(stderr, "qemu: could not load Milkymist One bios '%s'\n", bios_name); exit(1); diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index a8eee44e62..24f2068559 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -12,6 +12,7 @@ #include "hw/loader.h" #include "elf.h" #include "exec/address-spaces.h" +#include "sysemu/qtest.h" #define KERNEL_LOAD_ADDR 0x10000 #define AN5206_MBAR_ADDR 0x10000000 @@ -62,6 +63,9 @@ static void an5206_init(QEMUMachineInitArgs *args) /* Load kernel. */ if (!kernel_filename) { + if (qtest_enabled()) { + return; + } fprintf(stderr, "Kernel image must be specified\n"); exit(1); } diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index fb96fe8548..6e30c0b393 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -10,6 +10,7 @@ #include "qemu/timer.h" #include "hw/ptimer.h" #include "sysemu/sysemu.h" +#include "sysemu/qtest.h" #include "net/net.h" #include "hw/boards.h" #include "hw/loader.h" @@ -267,6 +268,9 @@ static void mcf5208evb_init(QEMUMachineInitArgs *args) /* Load kernel. */ if (!kernel_filename) { + if (qtest_enabled()) { + return; + } fprintf(stderr, "Kernel image must be specified\n"); exit(1); } diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 5b057f7880..2a7ea5c0f9 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -26,6 +26,7 @@ #include "qemu/option.h" #include "qemu/config-file.h" +#include "qemu/error-report.h" #include "qemu-common.h" #include "sysemu/device_tree.h" #include "sysemu/sysemu.h" @@ -39,6 +40,8 @@ static struct void (*machine_cpu_reset)(MicroBlazeCPU *); uint32_t bootstrap_pc; uint32_t cmdline; + uint32_t initrd_start; + uint32_t initrd_end; uint32_t fdt; } boot_info; @@ -49,6 +52,7 @@ static void main_cpu_reset(void *opaque) cpu_reset(CPU(cpu)); env->regs[5] = boot_info.cmdline; + env->regs[6] = boot_info.initrd_start; env->regs[7] = boot_info.fdt; env->sregs[SR_PC] = boot_info.bootstrap_pc; if (boot_info.machine_cpu_reset) { @@ -57,9 +61,11 @@ static void main_cpu_reset(void *opaque) } static int microblaze_load_dtb(hwaddr addr, - uint32_t ramsize, - const char *kernel_cmdline, - const char *dtb_filename) + uint32_t ramsize, + uint32_t initrd_start, + uint32_t initrd_end, + const char *kernel_cmdline, + const char *dtb_filename) { int fdt_size; void *fdt = NULL; @@ -80,6 +86,14 @@ static int microblaze_load_dtb(hwaddr addr, } } + if (initrd_start) { + qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_start); + + qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end", + initrd_end); + } + cpu_physical_memory_write(addr, fdt, fdt_size); return fdt_size; } @@ -90,7 +104,9 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) } void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, - uint32_t ramsize, const char *dtb_filename, + uint32_t ramsize, + const char *initrd_filename, + const char *dtb_filename, void (*machine_cpu_reset)(MicroBlazeCPU *)) { QemuOpts *machine_opts; @@ -151,14 +167,36 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, high = (ddr_base + kernel_size + 3) & ~3; } + if (initrd_filename) { + int initrd_size; + uint32_t initrd_offset; + + high = ROUND_UP(high + kernel_size, 4); + boot_info.initrd_start = high; + initrd_offset = boot_info.initrd_start - ddr_base; + initrd_size = load_image_targphys(initrd_filename, + boot_info.initrd_start, + ram_size - initrd_offset); + if (initrd_size < 0) { + error_report("qemu: could not load initrd '%s'\n", + initrd_filename); + exit(EXIT_FAILURE); + } + boot_info.initrd_end = boot_info.initrd_start + initrd_size; + high = ROUND_UP(high + initrd_size, 4); + } + boot_info.cmdline = high + 4096; if (kernel_cmdline && strlen(kernel_cmdline)) { pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); } /* Provide a device-tree. */ boot_info.fdt = boot_info.cmdline + 4096; - microblaze_load_dtb(boot_info.fdt, ram_size, kernel_cmdline, - dtb_filename); + microblaze_load_dtb(boot_info.fdt, ram_size, + boot_info.initrd_start, + boot_info.initrd_end, + kernel_cmdline, + dtb_filename); } } diff --git a/hw/microblaze/boot.h b/hw/microblaze/boot.h index b14ef2b992..0eb7f8e4f6 100644 --- a/hw/microblaze/boot.h +++ b/hw/microblaze/boot.h @@ -4,7 +4,9 @@ #include "hw/hw.h" void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, - uint32_t ramsize, const char *dtb_filename, + uint32_t ramsize, + const char *initrd_filename, + const char *dtb_filename, void (*machine_cpu_reset)(MicroBlazeCPU *)); #endif /* __MICROBLAZE_BOOT __ */ diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index e003c7c7b4..10970e0f3f 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -176,8 +176,10 @@ petalogix_ml605_init(QEMUMachineInitArgs *args) } } - microblaze_load_kernel(cpu, ddr_base, ram_size, BINARY_DEVICE_TREE_FILE, - machine_cpu_reset); + microblaze_load_kernel(cpu, ddr_base, ram_size, + args->initrd_filename, + BINARY_DEVICE_TREE_FILE, + machine_cpu_reset); } diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 00af2b5abc..ec6489c2d3 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -108,7 +108,9 @@ petalogix_s3adsp1800_init(QEMUMachineInitArgs *args) xilinx_ethlite_create(&nd_table[0], ETHLITE_BASEADDR, irq[1], 0, 0); microblaze_load_kernel(cpu, ddr_base, ram_size, - BINARY_DEVICE_TREE_FILE, machine_cpu_reset); + args->initrd_filename, + BINARY_DEVICE_TREE_FILE, + machine_cpu_reset); } static QEMUMachine petalogix_s3adsp1800_machine = { diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c index 242bab9779..239aa6ac8c 100644 --- a/hw/mips/mips_mipssim.c +++ b/hw/mips/mips_mipssim.c @@ -38,6 +38,7 @@ #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "qemu/error-report.h" +#include "sysemu/qtest.h" static struct _loaderparams { int ram_size; @@ -190,7 +191,8 @@ mips_mipssim_init(QEMUMachineInitArgs *args) } else { bios_size = -1; } - if ((bios_size < 0 || bios_size > BIOS_SIZE) && !kernel_filename) { + if ((bios_size < 0 || bios_size > BIOS_SIZE) && + !kernel_filename && !qtest_enabled()) { /* Bail out if we have neither a kernel image nor boot vector code. */ error_report("Could not load MIPS bios '%s', and no " "-kernel argument was specified", filename); diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 2578e2939d..f6743659f7 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -10,6 +10,9 @@ obj-$(CONFIG_VMPORT) += vmport.o # ARM devices common-obj-$(CONFIG_PL310) += arm_l2x0.o +common-obj-$(CONFIG_INTEGRATOR_DEBUG) += arm_integrator_debug.o +common-obj-$(CONFIG_A9SCU) += a9scu.o +common-obj-$(CONFIG_ARM11SCU) += arm11scu.o # PKUnity SoC devices common-obj-$(CONFIG_PUV3) += puv3_pm.o @@ -22,7 +25,6 @@ obj-$(CONFIG_LINUX) += vfio.o endif obj-$(CONFIG_REALVIEW) += arm_sysctl.o -obj-$(CONFIG_A9SCU) += a9scu.o obj-$(CONFIG_NSERIES) += cbus.o obj-$(CONFIG_ECCMEMCTL) += eccmemctl.o obj-$(CONFIG_EXYNOS4) += exynos4210_pmu.o @@ -36,7 +38,6 @@ obj-$(CONFIG_OMAP) += omap_gpmc.o obj-$(CONFIG_OMAP) += omap_l4.o obj-$(CONFIG_OMAP) += omap_sdrc.o obj-$(CONFIG_OMAP) += omap_tap.o -obj-$(CONFIG_PXA2XX) += pxa2xx_pcmcia.o obj-$(CONFIG_SLAVIO) += slavio_misc.o obj-$(CONFIG_ZYNQ) += zynq_slcr.o diff --git a/hw/misc/a9scu.c b/hw/misc/a9scu.c index 601b5733f2..4434945908 100644 --- a/hw/misc/a9scu.c +++ b/hw/misc/a9scu.c @@ -8,20 +8,7 @@ * This code is licensed under the GPL. */ -#include "hw/sysbus.h" - -/* A9MP private memory region. */ - -typedef struct A9SCUState { - SysBusDevice busdev; - MemoryRegion iomem; - uint32_t control; - uint32_t status; - uint32_t num_cpu; -} A9SCUState; - -#define TYPE_A9_SCU "a9-scu" -#define A9_SCU(obj) OBJECT_CHECK(A9SCUState, (obj), TYPE_A9_SCU) +#include "hw/misc/a9scu.h" static uint64_t a9_scu_read(void *opaque, hwaddr offset, unsigned size) @@ -114,12 +101,12 @@ static void a9_scu_reset(DeviceState *dev) s->control = 0; } -static void a9_scu_realize(DeviceState *dev, Error ** errp) +static void a9_scu_init(Object *obj) { - A9SCUState *s = A9_SCU(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + A9SCUState *s = A9_SCU(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - memory_region_init_io(&s->iomem, OBJECT(dev), &a9_scu_ops, s, + memory_region_init_io(&s->iomem, obj, &a9_scu_ops, s, "a9-scu", 0x100); sysbus_init_mmio(sbd, &s->iomem); } @@ -144,7 +131,6 @@ static void a9_scu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = a9_scu_realize; dc->props = a9_scu_properties; dc->vmsd = &vmstate_a9_scu; dc->reset = a9_scu_reset; @@ -154,6 +140,7 @@ static const TypeInfo a9_scu_info = { .name = TYPE_A9_SCU, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(A9SCUState), + .instance_init = a9_scu_init, .class_init = a9_scu_class_init, }; diff --git a/hw/misc/arm11scu.c b/hw/misc/arm11scu.c new file mode 100644 index 0000000000..a791675443 --- /dev/null +++ b/hw/misc/arm11scu.c @@ -0,0 +1,100 @@ +/* + * ARM11MPCore Snoop Control Unit (SCU) emulation + * + * Copyright (c) 2006-2007 CodeSourcery. + * Copyright (c) 2013 SUSE LINUX Products GmbH + * Written by Paul Brook and Andreas Färber + * + * This code is licensed under the GPL. + */ + +#include "hw/misc/arm11scu.h" + +static uint64_t mpcore_scu_read(void *opaque, hwaddr offset, + unsigned size) +{ + ARM11SCUState *s = (ARM11SCUState *)opaque; + int id; + /* SCU */ + switch (offset) { + case 0x00: /* Control. */ + return s->control; + case 0x04: /* Configuration. */ + id = ((1 << s->num_cpu) - 1) << 4; + return id | (s->num_cpu - 1); + case 0x08: /* CPU status. */ + return 0; + case 0x0c: /* Invalidate all. */ + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "mpcore_priv_read: Bad offset %x\n", (int)offset); + return 0; + } +} + +static void mpcore_scu_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + ARM11SCUState *s = (ARM11SCUState *)opaque; + /* SCU */ + switch (offset) { + case 0: /* Control register. */ + s->control = value & 1; + break; + case 0x0c: /* Invalidate all. */ + /* This is a no-op as cache is not emulated. */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "mpcore_priv_read: Bad offset %x\n", (int)offset); + } +} + +static const MemoryRegionOps mpcore_scu_ops = { + .read = mpcore_scu_read, + .write = mpcore_scu_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void arm11_scu_realize(DeviceState *dev, Error **errp) +{ +} + +static void arm11_scu_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ARM11SCUState *s = ARM11_SCU(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), + &mpcore_scu_ops, s, "mpcore-scu", 0x100); + sysbus_init_mmio(sbd, &s->iomem); +} + +static Property arm11_scu_properties[] = { + DEFINE_PROP_UINT32("num-cpu", ARM11SCUState, num_cpu, 1), + DEFINE_PROP_END_OF_LIST() +}; + +static void arm11_scu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = arm11_scu_realize; + dc->props = arm11_scu_properties; +} + +static const TypeInfo arm11_scu_type_info = { + .name = TYPE_ARM11_SCU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARM11SCUState), + .instance_init = arm11_scu_init, + .class_init = arm11_scu_class_init, +}; + +static void arm11_scu_register_types(void) +{ + type_register_static(&arm11_scu_type_info); +} + +type_init(arm11_scu_register_types) diff --git a/hw/misc/arm_integrator_debug.c b/hw/misc/arm_integrator_debug.c new file mode 100644 index 0000000000..99b720fbb9 --- /dev/null +++ b/hw/misc/arm_integrator_debug.c @@ -0,0 +1,99 @@ +/* + * LED, Switch and Debug control registers for ARM Integrator Boards + * + * This is currently a stub for this functionality but at least + * ensures something other than unassigned_mem_read() handles access + * to this area. + * + * The real h/w is described at: + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0159b/Babbfijf.html + * + * Copyright (c) 2013 Alex Bennée <alex@bennee.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 "hw/hw.h" +#include "hw/sysbus.h" +#include "exec/address-spaces.h" +#include "hw/misc/arm_integrator_debug.h" + +#define INTEGRATOR_DEBUG(obj) \ + OBJECT_CHECK(IntegratorDebugState, (obj), TYPE_INTEGRATOR_DEBUG) + +typedef struct { + SysBusDevice parent_obj; + + MemoryRegion iomem; +} IntegratorDebugState; + +static uint64_t intdbg_control_read(void *opaque, hwaddr offset, + unsigned size) +{ + switch (offset >> 2) { + case 0: /* ALPHA */ + case 1: /* LEDS */ + case 2: /* SWITCHES */ + qemu_log_mask(LOG_UNIMP, + "%s: returning zero from %" HWADDR_PRIx ":%u\n", + __func__, offset, size); + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset %" HWADDR_PRIx, + __func__, offset); + return 0; + } +} + +static void intdbg_control_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + switch (offset >> 2) { + case 1: /* ALPHA */ + case 2: /* LEDS */ + case 3: /* SWITCHES */ + /* Nothing interesting implemented yet. */ + qemu_log_mask(LOG_UNIMP, + "%s: ignoring write of %" PRIu64 + " to %" HWADDR_PRIx ":%u\n", + __func__, value, offset, size); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write of %" PRIu64 + " to bad offset %" HWADDR_PRIx "\n", + __func__, value, offset); + } +} + +static const MemoryRegionOps intdbg_control_ops = { + .read = intdbg_control_read, + .write = intdbg_control_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void intdbg_control_init(Object *obj) +{ + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IntegratorDebugState *s = INTEGRATOR_DEBUG(obj); + + memory_region_init_io(&s->iomem, NULL, &intdbg_control_ops, + NULL, "dbg-leds", 0x1000000); + sysbus_init_mmio(sd, &s->iomem); +} + +static const TypeInfo intdbg_info = { + .name = TYPE_INTEGRATOR_DEBUG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IntegratorDebugState), + .instance_init = intdbg_control_init, +}; + +static void intdbg_register_types(void) +{ + type_register_static(&intdbg_info); +} + +type_init(intdbg_register_types) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 2838866f45..8d144baa1e 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -133,7 +133,7 @@ static void ivshmem_update_irq(IVShmemState *s, int val) isr ? 1 : 0, s->intrstatus, s->intrmask); } - qemu_set_irq(d->irq[0], (isr != 0)); + pci_set_irq(d, (isr != 0)); } static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index b64e3bb7b4..226e2983d3 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -117,8 +117,19 @@ void pvpanic_init(ISABus *bus) isa_create_simple(bus, TYPE_ISA_PVPANIC_DEVICE); } +#define PVPANIC_IOPORT_PROP "ioport" + +uint16_t pvpanic_port(void) +{ + Object *o = object_resolve_path_type("", TYPE_ISA_PVPANIC_DEVICE, NULL); + if (!o) { + return 0; + } + return object_property_get_int(o, PVPANIC_IOPORT_PROP, NULL); +} + static Property pvpanic_isa_properties[] = { - DEFINE_PROP_UINT16("ioport", PVPanicState, ioport, 0x505), + DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicState, ioport, 0x505), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c index a2d5283ff9..fe95e03d1d 100644 --- a/hw/misc/vfio.c +++ b/hw/misc/vfio.c @@ -302,7 +302,7 @@ static void vfio_intx_interrupt(void *opaque) 'A' + vdev->intx.pin); vdev->intx.pending = true; - qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 1); + pci_irq_assert(&vdev->pdev); vfio_mmap_set_enabled(vdev, false); if (vdev->intx.mmap_timeout) { timer_mod(vdev->intx.mmap_timer, @@ -320,7 +320,7 @@ static void vfio_eoi(VFIODevice *vdev) vdev->host.bus, vdev->host.slot, vdev->host.function); vdev->intx.pending = false; - qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0); + pci_irq_deassert(&vdev->pdev); vfio_unmask_intx(vdev); } @@ -346,7 +346,7 @@ static void vfio_enable_intx_kvm(VFIODevice *vdev) qemu_set_fd_handler(irqfd.fd, NULL, NULL, vdev); vfio_mask_intx(vdev); vdev->intx.pending = false; - qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0); + pci_irq_deassert(&vdev->pdev); /* Get an eventfd for resample/unmask */ if (event_notifier_init(&vdev->intx.unmask, 0)) { @@ -422,7 +422,7 @@ static void vfio_disable_intx_kvm(VFIODevice *vdev) */ vfio_mask_intx(vdev); vdev->intx.pending = false; - qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0); + pci_irq_deassert(&vdev->pdev); /* Tell KVM to stop listening for an INTx irqfd */ if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) { @@ -493,6 +493,7 @@ static int vfio_enable_intx(VFIODevice *vdev) vfio_disable_interrupts(vdev); vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */ + pci_config_set_interrupt_pin(vdev->pdev.config, pin); #ifdef CONFIG_KVM /* @@ -552,7 +553,7 @@ static void vfio_disable_intx(VFIODevice *vdev) vfio_disable_intx_kvm(vdev); vfio_disable_irqindex(vdev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; - qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0); + pci_irq_deassert(&vdev->pdev); vfio_mmap_set_enabled(vdev, true); fd = event_notifier_get_fd(&vdev->intx.interrupt); diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 70a59fde7f..ae6359117d 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -328,7 +328,7 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val) } s->mit_irq_level = (pending_ints != 0); - qemu_set_irq(d->irq[0], s->mit_irq_level); + pci_set_irq(d, s->mit_irq_level); } static void @@ -528,8 +528,7 @@ putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse) n = cse + 1; if (sloc < n-1) { sum = net_checksum_add(n-css, data+css); - cpu_to_be16wu((uint16_t *)(data + sloc), - net_checksum_finish(sum)); + stw_be_p(data + sloc, net_checksum_finish(sum)); } } @@ -590,31 +589,28 @@ xmit_seg(E1000State *s) DBGOUT(TXSUM, "frames %d size %d ipcss %d\n", frames, tp->size, css); if (tp->ip) { // IPv4 - cpu_to_be16wu((uint16_t *)(tp->data+css+2), - tp->size - css); - cpu_to_be16wu((uint16_t *)(tp->data+css+4), + stw_be_p(tp->data+css+2, tp->size - css); + stw_be_p(tp->data+css+4, be16_to_cpup((uint16_t *)(tp->data+css+4))+frames); } else // IPv6 - cpu_to_be16wu((uint16_t *)(tp->data+css+4), - tp->size - css); + stw_be_p(tp->data+css+4, tp->size - css); css = tp->tucss; len = tp->size - css; DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len); if (tp->tcp) { sofar = frames * tp->mss; - cpu_to_be32wu((uint32_t *)(tp->data+css+4), // seq - be32_to_cpupu((uint32_t *)(tp->data+css+4))+sofar); + stl_be_p(tp->data+css+4, ldl_be_p(tp->data+css+4)+sofar); /* seq */ if (tp->paylen - sofar > tp->mss) tp->data[css + 13] &= ~9; // PSH, FIN } else // UDP - cpu_to_be16wu((uint16_t *)(tp->data+css+4), len); + stw_be_p(tp->data+css+4, len); if (tp->sum_needed & E1000_TXD_POPTS_TXSM) { unsigned int phsum; // add pseudo-header length before checksum calculation sp = (uint16_t *)(tp->data + tp->tucso); phsum = be16_to_cpup(sp) + len; phsum = (phsum >> 16) + (phsum & 0xffff); - cpu_to_be16wu(sp, phsum); + stw_be_p(sp, phsum); } tp->tso_frames++; } @@ -684,9 +680,9 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp) if (vlan_enabled(s) && is_vlan_txd(txd_lower) && (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) { tp->vlan_needed = 1; - cpu_to_be16wu((uint16_t *)(tp->vlan_header), + stw_be_p(tp->vlan_header, le16_to_cpup((uint16_t *)(s->mac_reg + VET))); - cpu_to_be16wu((uint16_t *)(tp->vlan_header + 2), + stw_be_p(tp->vlan_header + 2, le16_to_cpu(dp->upper.fields.special)); } @@ -1110,7 +1106,7 @@ mac_writereg(E1000State *s, int index, uint32_t val) s->mac_reg[index] = val; - if (index == RA + 1) { + if (index == RA || index == RA + 1) { macaddr[0] = cpu_to_le32(s->mac_reg[RA]); macaddr[1] = cpu_to_le32(s->mac_reg[RA + 1]); qemu_format_nic_info_str(qemu_get_queue(s->nic), (uint8_t *)macaddr); diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index ffa60d5c96..3b891ca340 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -409,7 +409,7 @@ static void disable_interrupt(EEPRO100State * s) { if (s->int_stat) { TRACE(INT, logout("interrupt disabled\n")); - qemu_irq_lower(s->dev.irq[0]); + pci_irq_deassert(&s->dev); s->int_stat = 0; } } @@ -418,7 +418,7 @@ static void enable_interrupt(EEPRO100State * s) { if (!s->int_stat) { TRACE(INT, logout("interrupt enabled\n")); - qemu_irq_raise(s->dev.irq[0]); + pci_irq_assert(&s->dev); s->int_stat = 1; } } diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index c96125895e..4c32e9ec25 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -467,7 +467,7 @@ static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, addr &= ~1; /* XXX: check exact behaviour if not even */ if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { - cpu_to_le32wu((uint32_t *)(s->mem + addr), val); + stl_le_p(s->mem + addr, val); } } @@ -497,7 +497,7 @@ static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) addr &= ~1; /* XXX: check exact behaviour if not even */ if (addr < 32 || (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { - return le32_to_cpupu((uint32_t *)(s->mem + addr)); + return ldl_le_p(s->mem + addr); } else { return 0xffffffff; } @@ -731,7 +731,7 @@ static int pci_ne2000_init(PCIDevice *pci_dev) s = &d->ne2000; ne2000_setup_io(s, DEVICE(pci_dev), 0x100); pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io); - s->irq = d->dev.irq[0]; + s->irq = pci_allocate_irq(&d->dev); qemu_macaddr_default_if_unset(&s->c.macaddr); ne2000_reset(s); @@ -752,6 +752,7 @@ static void pci_ne2000_exit(PCIDevice *pci_dev) memory_region_destroy(&s->io); qemu_del_nic(s->nic); + qemu_free_irq(s->irq); } static Property ne2000_properties[] = { diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 865f2f0c59..6a5d8064bb 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -282,6 +282,7 @@ static void pci_pcnet_uninit(PCIDevice *dev) { PCIPCNetState *d = PCI_PCNET(dev); + qemu_free_irq(d->state.irq); memory_region_destroy(&d->state.mmio); memory_region_destroy(&d->io_bar); timer_del(d->state.poll_timer); @@ -331,7 +332,7 @@ static int pci_pcnet_init(PCIDevice *pci_dev) pci_register_bar(pci_dev, 1, 0, &s->mmio); - s->irq = pci_dev->irq[0]; + s->irq = pci_allocate_irq(pci_dev); s->phys_mem_read = pci_physical_memory_read; s->phys_mem_write = pci_physical_memory_write; s->dma_opaque = pci_dev; diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 3225f3d3e5..7f2b4db449 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -716,7 +716,7 @@ static void rtl8139_update_irq(RTL8139State *s) DPRINTF("Set IRQ to %d (%04x %04x)\n", isr ? 1 : 0, s->IntrStatus, s->IntrMask); - qemu_set_irq(d->irq[0], (isr != 0)); + pci_set_irq(d, (isr != 0)); } static int rtl8139_RxWrap(RTL8139State *s) @@ -2741,10 +2741,7 @@ static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val) switch (addr) { - case MAC0 ... MAC0+4: - s->phys[addr - MAC0] = val; - break; - case MAC0+5: + case MAC0 ... MAC0+5: s->phys[addr - MAC0] = val; qemu_format_nic_info_str(qemu_get_queue(s->nic), s->phys); break; diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index f5963e2cbe..a8e29b3b42 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -185,6 +185,7 @@ static void smc91c111_release_packet(smc91c111_state *s, int packet) s->allocated &= ~(1 << packet); if (s->tx_alloc == 0x80) smc91c111_tx_alloc(s); + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } /* Flush the TX FIFO. */ diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 4ff04113db..1bd6f50aaa 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -531,6 +531,7 @@ static void spapr_vlan_class_init(ObjectClass *klass, void *data) k->dt_type = "network"; k->dt_compatible = "IBM,l-lan"; k->signal_mask = 0x1; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->props = spapr_vlan_properties; k->rtce_window_size = 0x10000000; dc->vmsd = &vmstate_spapr_llan; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 22dbd053d4..b75c753305 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -200,16 +200,16 @@ static void rxfilter_notify(NetClientState *nc) VirtIONet *n = qemu_get_nic_opaque(nc); if (nc->rxfilter_notify_enabled) { + gchar *path = object_get_canonical_path(OBJECT(n->qdev)); if (n->netclient_name) { event_data = qobject_from_jsonf("{ 'name': %s, 'path': %s }", - n->netclient_name, - object_get_canonical_path(OBJECT(n->qdev))); + n->netclient_name, path); } else { - event_data = qobject_from_jsonf("{ 'path': %s }", - object_get_canonical_path(OBJECT(n->qdev))); + event_data = qobject_from_jsonf("{ 'path': %s }", path); } monitor_protocol_event(QEVENT_NIC_RX_FILTER_CHANGED, event_data); qobject_decref(event_data); + g_free(path); /* disable event notification to avoid events flooding */ nc->rxfilter_notify_enabled = 0; @@ -657,7 +657,8 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, } if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { - s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs, + s = iov_to_buf(iov, iov_cnt, 0, + &n->mac_table.macs[n->mac_table.in_use * ETH_ALEN], mac_data.entries * ETH_ALEN); if (s != mac_data.entries * ETH_ALEN) { goto error; @@ -1601,7 +1602,7 @@ static int virtio_net_device_exit(DeviceState *qdev) if (q->tx_timer) { timer_del(q->tx_timer); timer_free(q->tx_timer); - } else { + } else if (q->tx_bh) { qemu_bh_delete(q->tx_bh); } } diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 49c2466434..19687aa03c 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -336,7 +336,7 @@ static bool _vmxnet3_assert_interrupt_line(VMXNET3State *s, uint32_t int_idx) } VMW_IRPRN("Asserting line for interrupt %u", int_idx); - qemu_set_irq(d->irq[int_idx], 1); + pci_irq_assert(d); return true; } @@ -356,7 +356,7 @@ static void _vmxnet3_deassert_interrupt_line(VMXNET3State *s, int lidx) assert(!s->msi_used || !msi_enabled(d)); VMW_IRPRN("Deasserting line for interrupt %u", lidx); - qemu_set_irq(d->irq[lidx], 0); + pci_irq_deassert(d); } static void vmxnet3_update_interrupt_line_state(VMXNET3State *s, int lidx) @@ -1299,6 +1299,12 @@ static void vmxnet3_update_features(VMXNET3State *s) } } +static bool vmxnet3_verify_intx(VMXNET3State *s, int intx) +{ + return s->msix_used || s->msi_used || (intx == + (pci_get_byte(s->parent_obj.config + PCI_INTERRUPT_PIN) - 1)); +} + static void vmxnet3_activate_device(VMXNET3State *s) { int i; @@ -1332,6 +1338,7 @@ static void vmxnet3_activate_device(VMXNET3State *s) s->event_int_idx = VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.eventIntrIdx); + assert(vmxnet3_verify_intx(s, s->event_int_idx)); VMW_CFPRN("Events interrupt line is %u", s->event_int_idx); s->auto_int_masking = @@ -1364,6 +1371,7 @@ static void vmxnet3_activate_device(VMXNET3State *s) /* Read interrupt number for this TX queue */ s->txq_descr[i].intr_idx = VMXNET3_READ_TX_QUEUE_DESCR8(qdescr_pa, conf.intrIdx); + assert(vmxnet3_verify_intx(s, s->txq_descr[i].intr_idx)); VMW_CFPRN("TX Queue %d interrupt: %d", i, s->txq_descr[i].intr_idx); @@ -1411,6 +1419,7 @@ static void vmxnet3_activate_device(VMXNET3State *s) /* Read interrupt number for this RX queue */ s->rxq_descr[i].intr_idx = VMXNET3_READ_TX_QUEUE_DESCR8(qd_pa, conf.intrIdx); + assert(vmxnet3_verify_intx(s, s->rxq_descr[i].intr_idx)); VMW_CFPRN("RX Queue %d interrupt: %d", i, s->rxq_descr[i].intr_idx); diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index d0820e507b..f5dc3ea845 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -42,6 +42,7 @@ typedef struct FWCfgEntry { uint8_t *data; void *callback_opaque; FWCfgCallback callback; + FWCfgReadCallback read_callback; } FWCfgEntry; struct FWCfgState { @@ -249,8 +250,12 @@ static uint8_t fw_cfg_read(FWCfgState *s) if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len) ret = 0; - else + else { + if (e->read_callback) { + e->read_callback(e->callback_opaque, s->cur_offset); + } ret = e->data[s->cur_offset++]; + } trace_fw_cfg_read(s, ret); return ret; @@ -381,7 +386,10 @@ static const VMStateDescription vmstate_fw_cfg = { } }; -void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len) +static void fw_cfg_add_bytes_read_callback(FWCfgState *s, uint16_t key, + FWCfgReadCallback callback, + void *callback_opaque, + void *data, size_t len) { int arch = !!(key & FW_CFG_ARCH_LOCAL); @@ -391,6 +399,13 @@ void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len) s->entries[arch][key].data = data; s->entries[arch][key].len = (uint32_t)len; + s->entries[arch][key].read_callback = callback; + s->entries[arch][key].callback_opaque = callback_opaque; +} + +void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len) +{ + fw_cfg_add_bytes_read_callback(s, key, NULL, NULL, data, len); } void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value) @@ -444,8 +459,9 @@ void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, s->entries[arch][key].callback = callback; } -void fw_cfg_add_file(FWCfgState *s, const char *filename, - void *data, size_t len) +void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, + FWCfgReadCallback callback, void *callback_opaque, + void *data, size_t len) { int i, index; size_t dsize; @@ -459,7 +475,8 @@ void fw_cfg_add_file(FWCfgState *s, const char *filename, index = be32_to_cpu(s->files->count); assert(index < FW_CFG_FILE_SLOTS); - fw_cfg_add_bytes(s, FW_CFG_FILE_FIRST + index, data, len); + fw_cfg_add_bytes_read_callback(s, FW_CFG_FILE_FIRST + index, + callback, callback_opaque, data, len); pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename); @@ -477,6 +494,12 @@ void fw_cfg_add_file(FWCfgState *s, const char *filename, s->files->count = cpu_to_be32(index+1); } +void fw_cfg_add_file(FWCfgState *s, const char *filename, + void *data, size_t len) +{ + fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len); +} + static void fw_cfg_machine_ready(struct Notifier *n, void *data) { size_t len; diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index eb4500e26f..beaad682ac 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -182,6 +182,7 @@ static void spapr_nvram_class_init(ObjectClass *klass, void *data) k->dt_name = "nvram"; k->dt_type = "nvram"; k->dt_compatible = "qemu,spapr-nvram"; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->props = spapr_nvram_properties; } diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c index 988ca20898..9c54945107 100644 --- a/hw/openrisc/cputimer.c +++ b/hw/openrisc/cputimer.c @@ -30,19 +30,28 @@ static int is_counting; void cpu_openrisc_count_update(OpenRISCCPU *cpu) { - uint64_t now, next; - uint32_t wait; + uint64_t now; - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (!is_counting) { - timer_del(cpu->env.timer); - last_clk = now; return; } - + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); cpu->env.ttcr += (uint32_t)muldiv64(now - last_clk, TIMER_FREQ, get_ticks_per_sec()); last_clk = now; +} + +void cpu_openrisc_timer_update(OpenRISCCPU *cpu) +{ + uint32_t wait; + uint64_t now, next; + + if (!is_counting) { + return; + } + + cpu_openrisc_count_update(cpu); + now = last_clk; if ((cpu->env.ttmr & TTMR_TP) <= (cpu->env.ttcr & TTMR_TP)) { wait = TTMR_TP - (cpu->env.ttcr & TTMR_TP) + 1; @@ -50,7 +59,6 @@ void cpu_openrisc_count_update(OpenRISCCPU *cpu) } else { wait = (cpu->env.ttmr & TTMR_TP) - (cpu->env.ttcr & TTMR_TP); } - next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ); timer_mod(cpu->env.timer, next); } @@ -63,8 +71,9 @@ void cpu_openrisc_count_start(OpenRISCCPU *cpu) void cpu_openrisc_count_stop(OpenRISCCPU *cpu) { - is_counting = 0; + timer_del(cpu->env.timer); cpu_openrisc_count_update(cpu); + is_counting = 0; } static void openrisc_timer_cb(void *opaque) @@ -84,15 +93,15 @@ static void openrisc_timer_cb(void *opaque) break; case TIMER_INTR: cpu->env.ttcr = 0; - cpu_openrisc_count_start(cpu); break; case TIMER_SHOT: cpu_openrisc_count_stop(cpu); break; case TIMER_CONT: - cpu_openrisc_count_start(cpu); break; } + + cpu_openrisc_timer_update(cpu); } void cpu_openrisc_clock_init(OpenRISCCPU *cpu) diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index a9392c7bdc..440e187c46 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -53,6 +53,7 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) if (err) { goto bridge_error; } + dev->config[PCI_INTERRUPT_PIN] = 0x1; memory_region_init(&bridge_dev->bar, OBJECT(dev), "shpc-bar", shpc_bar_size(dev)); err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0); if (err) { @@ -73,7 +74,6 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) * Check whether that works well. */ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar); - dev->config[PCI_INTERRUPT_PIN] = 0x1; return 0; msi_error: slotid_cap_cleanup(dev); diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index c041149320..edc974ece3 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -48,6 +48,7 @@ typedef struct I440FXState { PCIHostState parent_obj; PcPciInfo pci_info; uint64_t pci_hole64_size; + uint32_t short_root_bus; } I440FXState; #define PIIX_NUM_PIC_IRQS 16 /* i8259 * 2 */ @@ -416,6 +417,14 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, return b; } +PCIBus *find_i440fx(void) +{ + PCIHostState *s = OBJECT_CHECK(PCIHostState, + object_resolve_path("/machine/i440fx", NULL), + TYPE_PCI_HOST_BRIDGE); + return s ? s->bus : NULL; +} + /* PIIX3 PCI to ISA bridge */ static void piix3_set_irq_pic(PIIX3State *piix3, int pic_irq) { @@ -712,13 +721,19 @@ static const TypeInfo i440fx_info = { static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { + I440FXState *s = I440FX_PCI_HOST_BRIDGE(host_bridge); + /* For backwards compat with old device paths */ - return "0000"; + if (s->short_root_bus) { + return "0000"; + } + return "0000:00"; } static Property i440fx_props[] = { DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, I440FXState, pci_hole64_size, DEFAULT_PCI_HOLE64_SIZE), + DEFINE_PROP_UINT32("short_root_bus", I440FXState, short_root_bus, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index ad703a4bf7..c043998e32 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -61,8 +61,13 @@ static void q35_host_realize(DeviceState *dev, Error **errp) static const char *q35_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { - /* For backwards compat with old device paths */ - return "0000"; + Q35PCIHost *s = Q35_HOST_DEVICE(host_bridge); + + /* For backwards compat with old device paths */ + if (s->mch.short_root_bus) { + return "0000"; + } + return "0000:00"; } static void q35_host_get_pci_hole_start(Object *obj, Visitor *v, @@ -109,11 +114,22 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, visit_type_uint64(v, &w64.end, name, errp); } +static void q35_host_get_mmcfg_size(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + PCIExpressHost *e = PCIE_HOST_BRIDGE(obj); + uint32_t value = e->size; + + visit_type_uint32(v, &value, name, errp); +} + static Property mch_props[] = { - DEFINE_PROP_UINT64("MCFG", Q35PCIHost, parent_obj.base_addr, + DEFINE_PROP_UINT64(PCIE_HOST_MCFG_BASE, Q35PCIHost, parent_obj.base_addr, MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT), DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, Q35PCIHost, mch.pci_hole64_size, DEFAULT_PCI_HOLE64_SIZE), + DEFINE_PROP_UINT32("short_root_bus", Q35PCIHost, mch.short_root_bus, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -160,6 +176,10 @@ static void q35_host_initfn(Object *obj) q35_host_get_pci_hole64_end, NULL, NULL, NULL, NULL); + object_property_add(obj, PCIE_HOST_MCFG_SIZE, "int", + q35_host_get_mmcfg_size, + NULL, NULL, NULL, NULL); + /* Leave enough space for the biggest MCFG BAR */ /* TODO: this matches current bios behaviour, but * it's not a power of two, which means an MTRR @@ -375,6 +395,16 @@ static int mch_init(PCIDevice *d) return 0; } +uint64_t mch_mcfg_base(void) +{ + bool ambiguous; + Object *o = object_resolve_path_type("", TYPE_MCH_PCI_DEVICE, &ambiguous); + if (!o) { + return 0; + } + return MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT; +} + static void mch_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci/pci-hotplug-old.c b/hw/pci/pci-hotplug-old.c index 619fe473e8..8dbc3c1cab 100644 --- a/hw/pci/pci-hotplug-old.c +++ b/hw/pci/pci-hotplug-old.c @@ -248,7 +248,7 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon, } dev = pci_create(bus, devfn, "virtio-blk-pci"); if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) { - qdev_free(&dev->qdev); + object_unparent(OBJECT(dev)); dev = NULL; break; } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 00554a05ac..ed32059bf8 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -83,7 +83,7 @@ static const TypeInfo pcie_bus_info = { static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); static void pci_update_mappings(PCIDevice *d); -static void pci_set_irq(void *opaque, int irq_num, int level); +static void pci_irq_handler(void *opaque, int irq_num, int level); static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom); static void pci_del_option_rom(PCIDevice *pdev); @@ -161,7 +161,7 @@ void pci_device_deassert_intx(PCIDevice *dev) { int i; for (i = 0; i < PCI_NUM_PINS; ++i) { - qemu_set_irq(dev->irq[i], 0); + pci_irq_handler(dev, i, 0); } } @@ -863,14 +863,12 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, pci_dev->config_read = config_read; pci_dev->config_write = config_write; bus->devices[devfn] = pci_dev; - pci_dev->irq = qemu_allocate_irqs(pci_set_irq, pci_dev, PCI_NUM_PINS); pci_dev->version_id = 2; /* Current pci device vmstate version */ return pci_dev; } static void do_pci_unregister_device(PCIDevice *pci_dev) { - qemu_free_irqs(pci_dev->irq); pci_dev->bus->devices[pci_dev->devfn] = NULL; pci_config_free(pci_dev); @@ -1175,7 +1173,7 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) /* generic PCI irq support */ /* 0 <= irq_num <= 3. level must be 0 or 1 */ -static void pci_set_irq(void *opaque, int irq_num, int level) +static void pci_irq_handler(void *opaque, int irq_num, int level) { PCIDevice *pci_dev = opaque; int change; @@ -1191,6 +1189,24 @@ static void pci_set_irq(void *opaque, int irq_num, int level) pci_change_irq_level(pci_dev, irq_num, change); } +static inline int pci_intx(PCIDevice *pci_dev) +{ + return pci_get_byte(pci_dev->config + PCI_INTERRUPT_PIN) - 1; +} + +qemu_irq pci_allocate_irq(PCIDevice *pci_dev) +{ + int intx = pci_intx(pci_dev); + + return qemu_allocate_irq(pci_irq_handler, pci_dev, intx); +} + +void pci_set_irq(PCIDevice *pci_dev, int level) +{ + int intx = pci_intx(pci_dev); + pci_irq_handler(pci_dev, intx, level); +} + /* Special hooks used by device assignment */ void pci_bus_set_route_irq_fn(PCIBus *bus, pci_route_irq_fn route_intx_to_irq) { @@ -2264,7 +2280,7 @@ static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) Range *range = opaque; PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); uint16_t cmd = pci_get_word(dev->config + PCI_COMMAND); - int r; + int i; if (!(cmd & PCI_COMMAND_MEMORY)) { return; @@ -2283,17 +2299,21 @@ static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) range_extend(range, &pref_range); } } - for (r = 0; r < PCI_NUM_REGIONS; ++r) { - PCIIORegion *region = &dev->io_regions[r]; + for (i = 0; i < PCI_NUM_REGIONS; ++i) { + PCIIORegion *r = &dev->io_regions[i]; Range region_range; - if (!region->size || - (region->type & PCI_BASE_ADDRESS_SPACE_IO) || - !(region->type & PCI_BASE_ADDRESS_MEM_TYPE_64)) { + if (!r->size || + (r->type & PCI_BASE_ADDRESS_SPACE_IO) || + !(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64)) { + continue; + } + region_range.begin = pci_bar_address(dev, i, r->type, r->size); + region_range.end = region_range.begin + r->size; + + if (region_range.begin == PCI_BAR_UNMAPPED) { continue; } - region_range.begin = pci_get_quad(dev->config + pci_bar(dev, r)); - region_range.end = region_range.begin + region->size; region_range.begin = MAX(region_range.begin, 0x1ULL << 32); diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index e6b22b860f..290ababb8b 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -391,7 +391,7 @@ void pci_bridge_exitfn(PCIDevice *pci_dev) pci_bridge_region_cleanup(s, s->windows); memory_region_destroy(&s->address_space_mem); memory_region_destroy(&s->address_space_io); - /* qbus_free() is called automatically by qdev_free() */ + /* qbus_free() is called automatically during device deletion */ } /* diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 50af3c1dfe..ca60cf2177 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -187,7 +187,7 @@ static void hotplug_event_notify(PCIDevice *dev) } else if (msi_enabled(dev)) { msi_notify(dev, pcie_cap_flags_get_vector(dev)); } else { - qemu_set_irq(dev->irq[dev->exp.hpev_intx], dev->exp.hpev_notified); + pci_set_irq(dev, dev->exp.hpev_notified); } } @@ -195,7 +195,7 @@ static void hotplug_event_clear(PCIDevice *dev) { hotplug_event_update_event_status(dev); if (!msix_enabled(dev) && !msi_enabled(dev) && !dev->exp.hpev_notified) { - qemu_set_irq(dev->irq[dev->exp.hpev_intx], 0); + pci_irq_deassert(dev); } } @@ -251,7 +251,7 @@ static int pcie_cap_slot_hotplug(DeviceState *qdev, PCI_EXP_SLTSTA_PDS); pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); } else { - qdev_free(&pci_dev->qdev); + object_unparent(OBJECT(pci_dev)); pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index ca762ab09a..991502e517 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -285,7 +285,7 @@ static void pcie_aer_root_notify(PCIDevice *dev) } else if (msi_enabled(dev)) { msi_notify(dev, pcie_aer_root_get_vector(dev)); } else { - qemu_set_irq(dev->irq[dev->exp.aer_intx], 1); + pci_irq_assert(dev); } } @@ -425,7 +425,7 @@ static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err) /* 7.10.8 Header Log Register */ uint8_t *header_log = aer_cap + PCI_ERR_HEADER_LOG + i * sizeof err->header[0]; - cpu_to_be32wu((uint32_t*)header_log, err->header[i]); + stl_be_p(header_log, err->header[i]); } } else { assert(!(err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT)); @@ -439,7 +439,7 @@ static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err) /* 7.10.12 tlp prefix log register */ uint8_t *prefix_log = aer_cap + PCI_ERR_TLP_PREFIX_LOG + i * sizeof err->prefix[0]; - cpu_to_be32wu((uint32_t*)prefix_log, err->prefix[i]); + stl_be_p(prefix_log, err->prefix[i]); } errcap |= PCI_ERR_CAP_TLP; } else { @@ -768,7 +768,7 @@ void pcie_aer_root_write_config(PCIDevice *dev, uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); /* 6.2.4.1.2 Interrupt Generation */ if (!msix_enabled(dev) && !msi_enabled(dev)) { - qemu_set_irq(dev->irq[dev->exp.aer_intx], !!(root_cmd & enabled_cmd)); + pci_set_irq(dev, !!(root_cmd & enabled_cmd)); return; } diff --git a/hw/pci/pcie_host.c b/hw/pci/pcie_host.c index b70e5adc4b..c6e1b573e1 100644 --- a/hw/pci/pcie_host.c +++ b/hw/pci/pcie_host.c @@ -24,27 +24,6 @@ #include "hw/pci/pcie_host.h" #include "exec/address-spaces.h" -/* - * PCI express mmcfig address - * bit 20 - 28: bus number - * bit 15 - 19: device number - * bit 12 - 14: function number - * bit 0 - 11: offset in configuration space of a given device - */ -#define PCIE_MMCFG_SIZE_MAX (1ULL << 28) -#define PCIE_MMCFG_SIZE_MIN (1ULL << 20) -#define PCIE_MMCFG_BUS_BIT 20 -#define PCIE_MMCFG_BUS_MASK 0x1ff -#define PCIE_MMCFG_DEVFN_BIT 12 -#define PCIE_MMCFG_DEVFN_MASK 0xff -#define PCIE_MMCFG_CONFOFFSET_MASK 0xfff -#define PCIE_MMCFG_BUS(addr) (((addr) >> PCIE_MMCFG_BUS_BIT) & \ - PCIE_MMCFG_BUS_MASK) -#define PCIE_MMCFG_DEVFN(addr) (((addr) >> PCIE_MMCFG_DEVFN_BIT) & \ - PCIE_MMCFG_DEVFN_MASK) -#define PCIE_MMCFG_CONFOFFSET(addr) ((addr) & PCIE_MMCFG_CONFOFFSET_MASK) - - /* a helper function to get a PCIDevice for a given mmconfig address */ static inline PCIDevice *pcie_dev_find_by_mmcfg_addr(PCIBus *s, uint32_t mmcfg_addr) @@ -104,9 +83,6 @@ static const MemoryRegionOps pcie_mmcfg_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -/* pcie_host::base_addr == PCIE_BASE_ADDR_UNMAPPED when it isn't mapped. */ -#define PCIE_BASE_ADDR_UNMAPPED ((hwaddr)-1ULL) - int pcie_host_init(PCIExpressHost *e) { e->base_addr = PCIE_BASE_ADDR_UNMAPPED; diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index eb092fdb61..576244b9f6 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -172,7 +172,7 @@ static void shpc_interrupt_update(PCIDevice *d) if (msi_enabled(d) && shpc->msi_requested != level) msi_notify(d, 0); else - qemu_set_irq(d->irq[0], level); + pci_set_irq(d, level); shpc->msi_requested = level; } @@ -254,7 +254,7 @@ static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot) ++devfn) { PCIDevice *affected_dev = shpc->sec_bus->devices[devfn]; if (affected_dev) { - qdev_free(&affected_dev->qdev); + object_unparent(OBJECT(affected_dev)); } } } diff --git a/hw/pcmcia/Makefile.objs b/hw/pcmcia/Makefile.objs new file mode 100644 index 0000000000..4eac060c93 --- /dev/null +++ b/hw/pcmcia/Makefile.objs @@ -0,0 +1,2 @@ +common-obj-y += pcmcia.o +obj-$(CONFIG_PXA2XX) += pxa2xx.o diff --git a/hw/pcmcia/pcmcia.c b/hw/pcmcia/pcmcia.c new file mode 100644 index 0000000000..78efe5a67a --- /dev/null +++ b/hw/pcmcia/pcmcia.c @@ -0,0 +1,24 @@ +/* + * PCMCIA emulation + * + * Copyright 2013 SUSE LINUX Products GmbH + */ + +#include "qemu-common.h" +#include "hw/hw.h" +#include "hw/pcmcia.h" + +static const TypeInfo pcmcia_card_type_info = { + .name = TYPE_PCMCIA_CARD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PCMCIACardState), + .abstract = true, + .class_size = sizeof(PCMCIACardClass), +}; + +static void pcmcia_register_types(void) +{ + type_register_static(&pcmcia_card_type_info); +} + +type_init(pcmcia_register_types) diff --git a/hw/misc/pxa2xx_pcmcia.c b/hw/pcmcia/pxa2xx.c index ef71a2af74..8f17596cc3 100644 --- a/hw/misc/pxa2xx_pcmcia.c +++ b/hw/pcmcia/pxa2xx.c @@ -11,28 +11,38 @@ */ #include "hw/hw.h" +#include "hw/sysbus.h" #include "hw/pcmcia.h" #include "hw/arm/pxa.h" +#define TYPE_PXA2XX_PCMCIA "pxa2xx-pcmcia" +#define PXA2XX_PCMCIA(obj) \ + OBJECT_CHECK(PXA2xxPCMCIAState, obj, TYPE_PXA2XX_PCMCIA) struct PXA2xxPCMCIAState { + SysBusDevice parent_obj; + PCMCIASocket slot; - PCMCIACardState *card; + MemoryRegion container_mem; MemoryRegion common_iomem; MemoryRegion attr_iomem; MemoryRegion iomem; qemu_irq irq; qemu_irq cd_irq; + + PCMCIACardState *card; }; static uint64_t pxa2xx_pcmcia_common_read(void *opaque, hwaddr offset, unsigned size) { PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; + PCMCIACardClass *pcc; if (s->slot.attached) { - return s->card->common_read(s->card->state, offset); + pcc = PCMCIA_CARD_GET_CLASS(s->card); + return pcc->common_read(s->card, offset); } return 0; @@ -42,9 +52,11 @@ static void pxa2xx_pcmcia_common_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; + PCMCIACardClass *pcc; if (s->slot.attached) { - s->card->common_write(s->card->state, offset, value); + pcc = PCMCIA_CARD_GET_CLASS(s->card); + pcc->common_write(s->card, offset, value); } } @@ -52,9 +64,11 @@ static uint64_t pxa2xx_pcmcia_attr_read(void *opaque, hwaddr offset, unsigned size) { PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; + PCMCIACardClass *pcc; if (s->slot.attached) { - return s->card->attr_read(s->card->state, offset); + pcc = PCMCIA_CARD_GET_CLASS(s->card); + return pcc->attr_read(s->card, offset); } return 0; @@ -64,9 +78,11 @@ static void pxa2xx_pcmcia_attr_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; + PCMCIACardClass *pcc; if (s->slot.attached) { - s->card->attr_write(s->card->state, offset, value); + pcc = PCMCIA_CARD_GET_CLASS(s->card); + pcc->attr_write(s->card, offset, value); } } @@ -74,9 +90,11 @@ static uint64_t pxa2xx_pcmcia_io_read(void *opaque, hwaddr offset, unsigned size) { PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; + PCMCIACardClass *pcc; if (s->slot.attached) { - return s->card->io_read(s->card->state, offset); + pcc = PCMCIA_CARD_GET_CLASS(s->card); + return pcc->io_read(s->card, offset); } return 0; @@ -86,9 +104,11 @@ static void pxa2xx_pcmcia_io_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; + PCMCIACardClass *pcc; if (s->slot.attached) { - s->card->io_write(s->card->state, offset, value); + pcc = PCMCIA_CARD_GET_CLASS(s->card); + pcc->io_write(s->card, offset, value); } } @@ -122,15 +142,43 @@ static void pxa2xx_pcmcia_set_irq(void *opaque, int line, int level) PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, hwaddr base) { + DeviceState *dev; PXA2xxPCMCIAState *s; - s = (PXA2xxPCMCIAState *) - g_malloc0(sizeof(PXA2xxPCMCIAState)); + dev = qdev_create(NULL, TYPE_PXA2XX_PCMCIA); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + s = PXA2XX_PCMCIA(dev); + + if (base == 0x30000000) { + s->slot.slot_string = "PXA PC Card Socket 1"; + } else { + s->slot.slot_string = "PXA PC Card Socket 0"; + } + + qdev_init_nofail(dev); + + return s; +} + +static void pxa2xx_pcmcia_realize(DeviceState *dev, Error **errp) +{ + PXA2xxPCMCIAState *s = PXA2XX_PCMCIA(dev); + + pcmcia_socket_register(&s->slot); +} + +static void pxa2xx_pcmcia_initfn(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PXA2xxPCMCIAState *s = PXA2XX_PCMCIA(obj); + + memory_region_init(&s->container_mem, obj, "container", 0x10000000); + sysbus_init_mmio(sbd, &s->container_mem); /* Socket I/O Memory Space */ memory_region_init_io(&s->iomem, NULL, &pxa2xx_pcmcia_io_ops, s, "pxa2xx-pcmcia-io", 0x04000000); - memory_region_add_subregion(sysmem, base | 0x00000000, + memory_region_add_subregion(&s->container_mem, 0x00000000, &s->iomem); /* Then next 64 MB is reserved */ @@ -138,62 +186,68 @@ PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, /* Socket Attribute Memory Space */ memory_region_init_io(&s->attr_iomem, NULL, &pxa2xx_pcmcia_attr_ops, s, "pxa2xx-pcmcia-attribute", 0x04000000); - memory_region_add_subregion(sysmem, base | 0x08000000, + memory_region_add_subregion(&s->container_mem, 0x08000000, &s->attr_iomem); /* Socket Common Memory Space */ memory_region_init_io(&s->common_iomem, NULL, &pxa2xx_pcmcia_common_ops, s, "pxa2xx-pcmcia-common", 0x04000000); - memory_region_add_subregion(sysmem, base | 0x0c000000, + memory_region_add_subregion(&s->container_mem, 0x0c000000, &s->common_iomem); - if (base == 0x30000000) - s->slot.slot_string = "PXA PC Card Socket 1"; - else - s->slot.slot_string = "PXA PC Card Socket 0"; s->slot.irq = qemu_allocate_irqs(pxa2xx_pcmcia_set_irq, s, 1)[0]; - pcmcia_socket_register(&s->slot); - return s; + object_property_add_link(obj, "card", TYPE_PCMCIA_CARD, + (Object **)&s->card, NULL); } /* Insert a new card into a slot */ int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card) { PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - if (s->slot.attached) + PCMCIACardClass *pcc; + + if (s->slot.attached) { return -EEXIST; + } if (s->cd_irq) { qemu_irq_raise(s->cd_irq); } s->card = card; + pcc = PCMCIA_CARD_GET_CLASS(s->card); - s->slot.attached = 1; + s->slot.attached = true; s->card->slot = &s->slot; - s->card->attach(s->card->state); + pcc->attach(s->card); return 0; } /* Eject card from the slot */ -int pxa2xx_pcmcia_dettach(void *opaque) +int pxa2xx_pcmcia_detach(void *opaque) { PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - if (!s->slot.attached) + PCMCIACardClass *pcc; + + if (!s->slot.attached) { return -ENOENT; + } - s->card->detach(s->card->state); + pcc = PCMCIA_CARD_GET_CLASS(s->card); + pcc->detach(s->card); s->card->slot = NULL; s->card = NULL; - s->slot.attached = 0; + s->slot.attached = false; - if (s->irq) + if (s->irq) { qemu_irq_lower(s->irq); - if (s->cd_irq) + } + if (s->cd_irq) { qemu_irq_lower(s->cd_irq); + } return 0; } @@ -205,3 +259,25 @@ void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq) s->irq = irq; s->cd_irq = cd_irq; } + +static void pxa2xx_pcmcia_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = pxa2xx_pcmcia_realize; +} + +static const TypeInfo pxa2xx_pcmcia_type_info = { + .name = TYPE_PXA2XX_PCMCIA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PXA2xxPCMCIAState), + .instance_init = pxa2xx_pcmcia_initfn, + .class_init = pxa2xx_pcmcia_class_init, +}; + +static void pxa2xx_pcmcia_register_types(void) +{ + type_register_static(&pxa2xx_pcmcia_type_info); +} + +type_init(pxa2xx_pcmcia_register_types) diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 655e49906d..67597dfb88 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -110,8 +110,9 @@ static int bamboo_load_device_tree(hwaddr addr, qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", tb_freq); - ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); + rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); + return 0; out: diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 004184d841..7e53a5f977 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -62,7 +62,7 @@ * * We load our kernel at 4M, leaving space for SLOF initial image */ -#define FDT_MAX_SIZE 0x10000 +#define FDT_MAX_SIZE 0x40000 #define RTAS_MAX_SIZE 0x10000 #define FW_MAX_SIZE 0x400000 #define FW_FILE_NAME "slof.bin" @@ -120,7 +120,7 @@ int spapr_allocate_irq_block(int num, bool lsi, bool msi) * it has to be aligned to num to support multiple * MSI vectors. MSI-X is not affected by this. * The hint is used for the first IRQ, the rest should - * be allocated continously. + * be allocated continuously. */ if (msi) { assert((num == 1) || (num == 2) || (num == 4) || @@ -161,14 +161,33 @@ static XICSState *try_create_xics(const char *type, int nr_servers, return NULL; } - return XICS(dev); + return XICS_COMMON(dev); } static XICSState *xics_system_init(int nr_servers, int nr_irqs) { XICSState *icp = NULL; - icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs); + if (kvm_enabled()) { + QemuOpts *machine_opts = qemu_get_machine_opts(); + bool irqchip_allowed = qemu_opt_get_bool(machine_opts, + "kernel_irqchip", true); + bool irqchip_required = qemu_opt_get_bool(machine_opts, + "kernel_irqchip", false); + if (irqchip_allowed) { + icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs); + } + + if (irqchip_required && !icp) { + perror("Failed to create in-kernel XICS\n"); + abort(); + } + } + + if (!icp) { + icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs); + } + if (!icp) { perror("Failed to create XICS\n"); abort(); @@ -185,9 +204,8 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) int smt = kvmppc_smt_threads(); uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; - assert(spapr->cpu_model); - CPU_FOREACH(cpu) { + DeviceClass *dc = DEVICE_GET_CLASS(cpu); uint32_t associativity[] = {cpu_to_be32(0x5), cpu_to_be32(0x0), cpu_to_be32(0x0), @@ -199,7 +217,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) continue; } - snprintf(cpu_model, 32, "/cpus/%s@%x", spapr->cpu_model, + snprintf(cpu_model, 32, "/cpus/%s@%x", dc->fw_name, cpu->cpu_index); offset = fdt_path_offset(fdt, cpu_model); @@ -269,10 +287,10 @@ static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, } while (0) -static void *spapr_create_fdt_skel(const char *cpu_model, - hwaddr initrd_base, +static void *spapr_create_fdt_skel(hwaddr initrd_base, hwaddr initrd_size, hwaddr kernel_size, + bool little_endian, const char *boot_device, const char *kernel_cmdline, uint32_t epow_irq) @@ -286,7 +304,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model, char qemu_hypertas_prop[] = "hcall-memop1"; uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)}; uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; - char *modelname; int i, smt = kvmppc_smt_threads(); unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80}; @@ -326,6 +343,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model, 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_device) { _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); @@ -342,18 +362,10 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); - modelname = g_strdup(cpu_model); - - for (i = 0; i < strlen(modelname); i++) { - modelname[i] = toupper(modelname[i]); - } - - /* This is needed during FDT finalization */ - spapr->cpu_model = g_strdup(modelname); - CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + DeviceClass *dc = DEVICE_GET_CLASS(cs); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); int index = cs->cpu_index; uint32_t servers_prop[smp_threads]; @@ -370,7 +382,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, continue; } - nodename = g_strdup_printf("%s@%x", modelname, index); + nodename = g_strdup_printf("%s@%x", dc->fw_name, index); _FDT((fdt_begin_node(fdt, nodename))); @@ -418,6 +430,10 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s", gservers_prop, sizeof(gservers_prop)))); + if (env->spr_cb[SPR_PURR].oea_read) { + _FDT((fdt_property(fdt, "ibm,purr", NULL, 0))); + } + if (env->mmu_model & POWERPC_MMU_1TSEG) { _FDT((fdt_property(fdt, "ibm,processor-segment-sizes", segs, sizeof(segs)))); @@ -450,8 +466,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_end_node(fdt))); } - g_free(modelname); - _FDT((fdt_end_node(fdt))); /* RTAS */ @@ -1102,6 +1116,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) uint32_t initrd_base = 0; long kernel_size = 0, initrd_size = 0; long load_limit, rtas_limit, fw_size; + bool kernel_le = false; char *filename; msi_supported = true; @@ -1175,8 +1190,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) } env = &cpu->env; - xics_cpu_setup(spapr->icp, cpu); - /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, TIMEBASE_FREQ); @@ -1190,6 +1203,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) kvmppc_set_papr(cpu); } + xics_cpu_setup(spapr->icp, cpu); + qemu_register_reset(spapr_cpu_reset, cpu); } @@ -1282,6 +1297,12 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); if (kernel_size < 0) { + kernel_size = load_elf(kernel_filename, + translate_kernel_address, NULL, + NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0); + kernel_le = kernel_size > 0; + } + if (kernel_size < 0) { kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, load_limit - KERNEL_LOAD_ADDR); @@ -1329,9 +1350,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) &savevm_htab_handlers, spapr); /* Prepare the device tree */ - spapr->fdt_skel = spapr_create_fdt_skel(cpu_model, - initrd_base, initrd_size, - kernel_size, + spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size, + kernel_size, kernel_le, boot_device, kernel_cmdline, spapr->epow_irq); assert(spapr->fdt_skel != NULL); diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index f10ba8a932..f755a53923 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -521,9 +521,9 @@ static target_ulong h_rtas(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong rtas_r3 = args[0]; - uint32_t token = ldl_be_phys(rtas_r3); - uint32_t nargs = ldl_be_phys(rtas_r3 + 4); - uint32_t nret = ldl_be_phys(rtas_r3 + 8); + uint32_t token = rtas_ld(rtas_r3, 0); + uint32_t nargs = rtas_ld(rtas_r3, 1); + uint32_t nret = rtas_ld(rtas_r3, 2); return spapr_rtas_call(cpu, spapr, token, nargs, rtas_r3 + 12, nret, rtas_r3 + 12 + 4*nargs); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 9b6ee32acf..edb4cb0413 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -432,6 +432,17 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); } +static PCIINTxRoute spapr_route_intx_pin_to_irq(void *opaque, int pin) +{ + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(opaque); + PCIINTxRoute route; + + route.mode = PCI_INTX_ENABLED; + route.irq = sphb->lsi_table[pin].irq; + + return route; +} + /* * MSI/MSIX memory region implementation. * The handler handles both MSI and MSIX. @@ -610,6 +621,8 @@ static int spapr_phb_init(SysBusDevice *s) pci_setup_iommu(bus, spapr_pci_dma_iommu, sphb); + pci_bus_set_route_irq_fn(bus, spapr_route_intx_pin_to_irq); + QLIST_INSERT_HEAD(&spapr->phbs, sphb, list); /* Initialize the LSI table */ diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index cd67db5e5b..f93a81c7cd 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1239,7 +1239,7 @@ static int virtio_ccw_busdev_unplug(DeviceState *dev) css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); - qdev_free(dev); + object_unparent(OBJECT(dev)); return 0; } diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index 99bf8ec446..48c8b82350 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -361,7 +361,7 @@ static int esp_pci_scsi_init(PCIDevice *dev) "esp-io", 0x80); pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io); - s->irq = dev->irq[0]; + s->irq = pci_allocate_irq(dev); scsi_bus_new(&s->bus, sizeof(s->bus), d, &esp_pci_scsi_info, NULL); if (!d->hotplugged) { @@ -378,6 +378,7 @@ static void esp_pci_scsi_uninit(PCIDevice *d) { PCIESPState *pci = PCI_ESP(d); + qemu_free_irq(pci->esp.irq); memory_region_destroy(&pci->io); } diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 36e5f50360..cb30414849 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -437,7 +437,7 @@ static void lsi_update_irq(LSIState *s) level, s->dstat, s->sist1, s->sist0); last_level = level; } - qemu_set_irq(d->irq[0], level); + pci_set_irq(d, level); if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) { DPRINTF("Handled IRQs & disconnected, looking for pending " diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 09b51b387b..7c5a1a2b3a 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -535,7 +535,7 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context) msix_notify(pci_dev, 0); } else { trace_megasas_irq_raise(); - qemu_irq_raise(pci_dev->irq[0]); + pci_irq_assert(pci_dev); } } } else { @@ -1936,7 +1936,7 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, s->intr_mask = val; if (!megasas_intr_enabled(s) && !msix_enabled(pci_dev)) { trace_megasas_irq_lower(); - qemu_irq_lower(pci_dev->irq[0]); + pci_irq_deassert(pci_dev); } if (megasas_intr_enabled(s)) { trace_megasas_intr_enabled(); @@ -1952,7 +1952,7 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, stl_le_phys(s->producer_pa, s->reply_queue_head); if (!msix_enabled(pci_dev)) { trace_megasas_irq_lower(); - qemu_irq_lower(pci_dev->irq[0]); + pci_irq_deassert(pci_dev); } } break; diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 24ec52f8f9..ea916d1466 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -178,7 +178,7 @@ static int scsi_qdev_init(DeviceState *qdev) d = scsi_device_find(bus, dev->channel, dev->id, dev->lun); assert(d); if (d->lun == dev->lun && dev != d) { - qdev_free(&d->qdev); + object_unparent(OBJECT(d)); } } @@ -231,13 +231,13 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, } if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) { error_setg(errp, "Setting drive property failed"); - qdev_free(dev); + object_unparent(OBJECT(dev)); return NULL; } object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); - qdev_free(dev); + object_unparent(OBJECT(dev)); return NULL; } return SCSI_DEVICE(dev); diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 2a26042701..c0c46d7f7c 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -1223,6 +1223,7 @@ static void spapr_vscsi_class_init(ObjectClass *klass, void *data) k->dt_type = "vscsi"; k->dt_compatible = "IBM,v-scsi"; k->signal_mask = 0x00000001; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->props = spapr_vscsi_properties; k->rtce_window_size = 0x10000000; dc->vmsd = &vmstate_spapr_vscsi; diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 819d671ba2..94b328f186 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -330,7 +330,7 @@ pvscsi_update_irq_status(PVSCSIState *s) return; } - qemu_set_irq(d->irq[0], !!should_raise); + pci_set_irq(d, !!should_raise); } static void diff --git a/hw/sh4/shix.c b/hw/sh4/shix.c index 1ff37f54a0..904a966700 100644 --- a/hw/sh4/shix.c +++ b/hw/sh4/shix.c @@ -30,9 +30,11 @@ #include "hw/hw.h" #include "hw/sh4/sh.h" #include "sysemu/sysemu.h" +#include "sysemu/qtest.h" #include "hw/boards.h" #include "hw/loader.h" #include "exec/address-spaces.h" +#include "qemu/error-report.h" #define BIOS_FILENAME "shix_bios.bin" #define BIOS_ADDRESS 0xA0000000 @@ -50,7 +52,6 @@ static void shix_init(QEMUMachineInitArgs *args) if (!cpu_model) cpu_model = "any"; - printf("Initializing CPU\n"); cpu = cpu_sh4_init(cpu_model); if (cpu == NULL) { fprintf(stderr, "Unable to find CPU definition\n"); @@ -58,16 +59,13 @@ static void shix_init(QEMUMachineInitArgs *args) } /* Allocate memory space */ - printf("Allocating ROM\n"); memory_region_init_ram(rom, NULL, "shix.rom", 0x4000); vmstate_register_ram_global(rom); memory_region_set_readonly(rom, true); memory_region_add_subregion(sysmem, 0x00000000, rom); - printf("Allocating SDRAM 1\n"); memory_region_init_ram(&sdram[0], NULL, "shix.sdram1", 0x01000000); vmstate_register_ram_global(&sdram[0]); memory_region_add_subregion(sysmem, 0x08000000, &sdram[0]); - printf("Allocating SDRAM 2\n"); memory_region_init_ram(&sdram[1], NULL, "shix.sdram2", 0x01000000); vmstate_register_ram_global(&sdram[1]); memory_region_add_subregion(sysmem, 0x0c000000, &sdram[1]); @@ -75,20 +73,16 @@ static void shix_init(QEMUMachineInitArgs *args) /* Load BIOS in 0 (and access it through P2, 0xA0000000) */ if (bios_name == NULL) bios_name = BIOS_FILENAME; - printf("%s: load BIOS '%s'\n", __func__, bios_name); ret = load_image_targphys(bios_name, 0, 0x4000); - if (ret < 0) { /* Check bios size */ - fprintf(stderr, "ret=%d\n", ret); - fprintf(stderr, "qemu: could not load SHIX bios '%s'\n", - bios_name); - exit(1); + if (ret < 0 && !qtest_enabled()) { + error_report("Could not load SHIX bios '%s'", bios_name); + exit(1); } /* Register peripherals */ s = sh7750_init(cpu, sysmem); /* XXXXX Check success */ tc58128_init(s, "shix_linux_nand.bin", NULL); - fprintf(stderr, "initialization terminated\n"); } static QEMUMachine shix_machine = { diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index 390f3e4bda..c583c3d0c5 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -26,6 +26,7 @@ #include "hw/ptimer.h" #include "sysemu/char.h" #include "sysemu/sysemu.h" +#include "sysemu/qtest.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" @@ -178,7 +179,7 @@ static void leon3_generic_hw_init(QEMUMachineInitArgs *args) fprintf(stderr, "qemu: could not load prom '%s'\n", filename); exit(1); } - } else if (kernel_filename == NULL) { + } else if (kernel_filename == NULL && !qtest_enabled()) { fprintf(stderr, "Can't read bios image %s\n", filename); exit(1); } diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index 8020c9f4b5..d9f9494f26 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -19,7 +19,7 @@ * with this program; if not, see <http://www.gnu.org/licenses/>. */ -#include "hw/sysbus.h" +#include "hw/timer/arm_mptimer.h" #include "qemu/timer.h" #include "qom/cpu.h" @@ -27,34 +27,6 @@ * which is used in both the ARM11MPCore and Cortex-A9MP. */ -#define MAX_CPUS 4 - -/* State of a single timer or watchdog block */ -typedef struct { - uint32_t count; - uint32_t load; - uint32_t control; - uint32_t status; - int64_t tick; - QEMUTimer *timer; - qemu_irq irq; - MemoryRegion iomem; -} TimerBlock; - -#define TYPE_ARM_MPTIMER "arm_mptimer" -#define ARM_MPTIMER(obj) \ - OBJECT_CHECK(ARMMPTimerState, (obj), TYPE_ARM_MPTIMER) - -typedef struct { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - uint32_t num_cpu; - TimerBlock timerblock[MAX_CPUS]; - MemoryRegion iomem; -} ARMMPTimerState; - static inline int get_current_cpu(ARMMPTimerState *s) { if (current_cpu->cpu_index >= s->num_cpu) { @@ -225,13 +197,24 @@ static void arm_mptimer_reset(DeviceState *dev) } } -static int arm_mptimer_init(SysBusDevice *dev) +static void arm_mptimer_init(Object *obj) +{ + ARMMPTimerState *s = ARM_MPTIMER(obj); + + memory_region_init_io(&s->iomem, obj, &arm_thistimer_ops, s, + "arm_mptimer_timer", 0x20); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); +} + +static void arm_mptimer_realize(DeviceState *dev, Error **errp) { + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); ARMMPTimerState *s = ARM_MPTIMER(dev); int i; - if (s->num_cpu < 1 || s->num_cpu > MAX_CPUS) { - hw_error("%s: num-cpu must be between 1 and %d\n", __func__, MAX_CPUS); + if (s->num_cpu < 1 || s->num_cpu > ARM_MPTIMER_MAX_CPUS) { + hw_error("%s: num-cpu must be between 1 and %d\n", + __func__, ARM_MPTIMER_MAX_CPUS); } /* We implement one timer block per CPU, and expose multiple MMIO regions: * * region 0 is "timer for this core" @@ -243,19 +226,14 @@ static int arm_mptimer_init(SysBusDevice *dev) * * timer for core 1 * and so on. */ - memory_region_init_io(&s->iomem, OBJECT(s), &arm_thistimer_ops, s, - "arm_mptimer_timer", 0x20); - sysbus_init_mmio(dev, &s->iomem); for (i = 0; i < s->num_cpu; i++) { TimerBlock *tb = &s->timerblock[i]; tb->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timerblock_tick, tb); - sysbus_init_irq(dev, &tb->irq); + sysbus_init_irq(sbd, &tb->irq); memory_region_init_io(&tb->iomem, OBJECT(s), &timerblock_ops, tb, "arm_mptimer_timerblock", 0x20); - sysbus_init_mmio(dev, &tb->iomem); + sysbus_init_mmio(sbd, &tb->iomem); } - - return 0; } static const VMStateDescription vmstate_timerblock = { @@ -292,9 +270,8 @@ static Property arm_mptimer_properties[] = { static void arm_mptimer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - sbc->init = arm_mptimer_init; + dc->realize = arm_mptimer_realize; dc->vmsd = &vmstate_arm_mptimer; dc->reset = arm_mptimer_reset; dc->no_user = 1; @@ -305,6 +282,7 @@ static const TypeInfo arm_mptimer_info = { .name = TYPE_ARM_MPTIMER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(ARMMPTimerState), + .instance_init = arm_mptimer_init, .class_init = arm_mptimer_class_init, }; diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index fcd22aea59..2eb75ea945 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -757,6 +757,11 @@ static void hpet_device_class_init(ObjectClass *klass, void *data) dc->props = hpet_device_properties; } +bool hpet_find(void) +{ + return object_resolve_path_type("", TYPE_HPET, NULL); +} + static const TypeInfo hpet_device_info = { .name = TYPE_HPET, .parent = TYPE_SYS_BUS_DEVICE, diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 7230a6e4fa..b0116381c0 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -70,7 +70,6 @@ typedef struct RTCState { uint64_t last_update; int64_t offset; qemu_irq irq; - qemu_irq sqw_irq; int it_shift; /* periodic timer */ QEMUTimer *periodic_timer; @@ -151,8 +150,7 @@ static void periodic_timer_update(RTCState *s, int64_t current_time) period_code = s->cmos_data[RTC_REG_A] & 0x0f; if (period_code != 0 - && ((s->cmos_data[RTC_REG_B] & REG_B_PIE) - || ((s->cmos_data[RTC_REG_B] & REG_B_SQWE) && s->sqw_irq))) { + && (s->cmos_data[RTC_REG_B] & REG_B_PIE)) { if (period_code <= 2) period_code += 7; /* period in 32 Khz cycles */ @@ -202,11 +200,6 @@ static void rtc_periodic_timer(void *opaque) #endif qemu_irq_raise(s->irq); } - if (s->cmos_data[RTC_REG_B] & REG_B_SQWE) { - /* Not square wave at all but we don't want 2048Hz interrupts! - Must be seen as a pulse. */ - qemu_irq_raise(s->sqw_irq); - } } /* handle update-ended timer */ diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index a9000617b4..e05cbc131e 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -17,6 +17,7 @@ #include "hw/boards.h" #include "hw/loader.h" #include "hw/i386/pc.h" +#include "sysemu/qtest.h" #undef DEBUG_PUV3 #include "hw/unicore32/puv3.h" @@ -84,6 +85,9 @@ static void puv3_load_kernel(const char *kernel_filename) { int size; + if (kernel_filename == NULL && qtest_enabled()) { + return; + } assert(kernel_filename != NULL); /* only zImage format supported */ diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 72d5b92225..ca329bef29 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -356,8 +356,9 @@ void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) void usb_unregister_port(USBBus *bus, USBPort *port) { - if (port->dev) - qdev_free(&port->dev->qdev); + if (port->dev) { + object_unparent(OBJECT(port->dev)); + } QTAILQ_REMOVE(&bus->free, port, next); bus->nfree--; } @@ -505,7 +506,7 @@ int usb_device_delete_addr(int busnr, int addr) return -1; dev = port->dev; - qdev_free(&dev->qdev); + object_unparent(OBJECT(dev)); return 0; } diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 660d7743fe..4c532b7d6a 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -637,7 +637,6 @@ typedef struct USBNetState { unsigned int out_ptr; uint8_t out_buf[2048]; - USBPacket *inpkt; unsigned int in_ptr, in_len; uint8_t in_buf[2048]; diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 1d81ac2a6b..c434c5680f 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -703,7 +703,7 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename) return NULL; } if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) { - qdev_free(&dev->qdev); + object_unparent(OBJECT(dev)); return NULL; } if (qdev_init(&dev->qdev) < 0) diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 4d21a0b7bb..0c985942f9 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -60,7 +60,7 @@ static int usb_ehci_pci_initfn(PCIDevice *dev) pci_conf[0x6e] = 0x00; pci_conf[0x6f] = 0xc0; /* USBLEFCTLSTS */ - s->irq = dev->irq[3]; + s->irq = pci_allocate_irq(dev); s->as = pci_get_address_space(dev); usb_ehci_realize(s, DEVICE(dev), NULL); diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 0396e334ed..e38cdebfec 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -1946,7 +1946,7 @@ static int usb_ohci_initfn_pci(PCIDevice *dev) pci_get_address_space(dev)) != 0) { return -1; } - ohci->state.irq = dev->irq[0]; + ohci->state.irq = pci_allocate_irq(dev); pci_register_bar(dev, 0, 0, &ohci->state.mem); return 0; diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index becc7faec1..238d1d2b5f 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -164,7 +164,6 @@ struct UHCIState { /* Interrupts that should be raised at the end of the current frame. */ uint32_t pending_int_mask; - int irq_pin; /* Active packets */ QTAILQ_HEAD(, UHCIQueue) queues; @@ -381,7 +380,7 @@ static void uhci_update_irq(UHCIState *s) } else { level = 0; } - qemu_set_irq(s->dev.irq[s->irq_pin], level); + pci_set_irq(&s->dev, level); } static void uhci_reset(void *opaque) @@ -1240,8 +1239,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev) /* TODO: reset value should be 0. */ pci_conf[USB_SBRN] = USB_RELEASE_1; // release number - s->irq_pin = u->info.irq_pin; - pci_config_set_interrupt_pin(pci_conf, s->irq_pin + 1); + pci_config_set_interrupt_pin(pci_conf, u->info.irq_pin + 1); if (s->masterbus) { USBPort *ports[NB_PORTS]; diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 469c24d768..835f65ed81 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -346,7 +346,6 @@ typedef struct XHCITransfer { QEMUSGList sgl; bool running_async; bool running_retry; - bool cancelled; bool complete; bool int_req; unsigned int iso_pkts; @@ -374,7 +373,6 @@ struct XHCIStreamContext { dma_addr_t pctx; unsigned int sct; XHCIRing ring; - XHCIStreamContext *sstreams; }; struct XHCIEPContext { @@ -449,7 +447,6 @@ struct XHCIState { /*< public >*/ USBBus bus; - qemu_irq irq; MemoryRegion mem; MemoryRegion mem_cap; MemoryRegion mem_oper; @@ -507,6 +504,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid, unsigned int streamid); static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); +static void xhci_xfer_report(XHCITransfer *xfer); static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v); static USBEndpoint *xhci_epid_to_usbep(XHCIState *xhci, @@ -739,7 +737,7 @@ static void xhci_intx_update(XHCIState *xhci) } trace_usb_xhci_irq_intx(level); - qemu_set_irq(xhci->irq, level); + pci_set_irq(pci_dev, level); } static void xhci_msix_update(XHCIState *xhci, int v) @@ -797,7 +795,7 @@ static void xhci_intr_raise(XHCIState *xhci, int v) if (v == 0) { trace_usb_xhci_irq_intx(1); - qemu_set_irq(xhci->irq, 1); + pci_irq_assert(pci_dev); } } @@ -1133,7 +1131,6 @@ static void xhci_reset_streams(XHCIEPContext *epctx) for (i = 0; i < epctx->nr_pstreams; i++) { epctx->pstreams[i].sct = -1; - g_free(epctx->pstreams[i].sstreams); } } @@ -1146,15 +1143,8 @@ static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base) static void xhci_free_streams(XHCIEPContext *epctx) { - int i; - assert(epctx->pstreams != NULL); - if (!epctx->lsa) { - for (i = 0; i < epctx->nr_pstreams; i++) { - g_free(epctx->pstreams[i].sstreams); - } - } g_free(epctx->pstreams); epctx->pstreams = NULL; epctx->nr_pstreams = 0; @@ -1196,6 +1186,7 @@ static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, XHCIStreamContext *sctx, uint32_t state) { + XHCIRing *ring = NULL; uint32_t ctx[5]; uint32_t ctx2[2]; @@ -1206,6 +1197,7 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, /* update ring dequeue ptr */ if (epctx->nr_pstreams) { if (sctx != NULL) { + ring = &sctx->ring; xhci_dma_read_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); ctx2[0] &= 0xe; ctx2[0] |= sctx->ring.dequeue | sctx->ring.ccs; @@ -1213,8 +1205,12 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, xhci_dma_write_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2)); } } else { - ctx[2] = epctx->ring.dequeue | epctx->ring.ccs; - ctx[3] = (epctx->ring.dequeue >> 16) >> 16; + ring = &epctx->ring; + } + if (ring) { + ctx[2] = ring->dequeue | ring->ccs; + ctx[3] = (ring->dequeue >> 16) >> 16; + DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n", epctx->pctx, state, ctx[3], ctx[2]); } @@ -1312,15 +1308,18 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, return CC_SUCCESS; } -static int xhci_ep_nuke_one_xfer(XHCITransfer *t) +static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) { int killed = 0; + if (report && (t->running_async || t->running_retry)) { + t->status = report; + xhci_xfer_report(t); + } + if (t->running_async) { usb_cancel_packet(&t->packet); t->running_async = 0; - t->cancelled = 1; - DPRINTF("xhci: cancelling transfer, waiting for it to complete\n"); killed = 1; } if (t->running_retry) { @@ -1330,6 +1329,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t) timer_del(epctx->kick_timer); } t->running_retry = 0; + killed = 1; } if (t->trbs) { g_free(t->trbs); @@ -1342,7 +1342,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t) } static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, - unsigned int epid) + unsigned int epid, TRBCCode report) { XHCISlot *slot; XHCIEPContext *epctx; @@ -1363,7 +1363,10 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, xferi = epctx->next_xfer; for (i = 0; i < TD_QUEUE; i++) { - killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]); + killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi], report); + if (killed) { + report = 0; /* Only report once */ + } epctx->transfers[xferi].packet.ep = NULL; xferi = (xferi + 1) % TD_QUEUE; } @@ -1393,7 +1396,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, return CC_SUCCESS; } - xhci_ep_nuke_xfers(xhci, slotid, epid); + xhci_ep_nuke_xfers(xhci, slotid, epid, 0); epctx = slot->eps[epid-1]; @@ -1435,7 +1438,7 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid, return CC_EP_NOT_ENABLED_ERROR; } - if (xhci_ep_nuke_xfers(xhci, slotid, epid) > 0) { + if (xhci_ep_nuke_xfers(xhci, slotid, epid, CC_STOPPED) > 0) { fprintf(stderr, "xhci: FIXME: endpoint stopped w/ xfers running, " "data might be lost\n"); } @@ -1480,7 +1483,7 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, return CC_CONTEXT_STATE_ERROR; } - if (xhci_ep_nuke_xfers(xhci, slotid, epid) > 0) { + if (xhci_ep_nuke_xfers(xhci, slotid, epid, 0) > 0) { fprintf(stderr, "xhci: FIXME: endpoint reset w/ xfers running, " "data might be lost\n"); } @@ -1737,14 +1740,12 @@ static int xhci_complete_packet(XHCITransfer *xfer) xfer->running_async = 1; xfer->running_retry = 0; xfer->complete = 0; - xfer->cancelled = 0; return 0; } else if (xfer->packet.status == USB_RET_NAK) { trace_usb_xhci_xfer_nak(xfer); xfer->running_async = 0; xfer->running_retry = 1; xfer->complete = 0; - xfer->cancelled = 0; return 0; } else { xfer->running_async = 0; @@ -2475,7 +2476,7 @@ static void xhci_detach_slot(XHCIState *xhci, USBPort *uport) for (ep = 0; ep < 31; ep++) { if (xhci->slots[slot].eps[ep]) { - xhci_ep_nuke_xfers(xhci, slot+1, ep+1); + xhci_ep_nuke_xfers(xhci, slot + 1, ep + 1, 0); } } xhci->slots[slot].uport = NULL; @@ -3290,7 +3291,7 @@ static void xhci_complete(USBPort *port, USBPacket *packet) XHCITransfer *xfer = container_of(packet, XHCITransfer, packet); if (packet->status == USB_RET_REMOVE_FROM_QUEUE) { - xhci_ep_nuke_one_xfer(xfer); + xhci_ep_nuke_one_xfer(xfer, 0); return; } xhci_complete_packet(xfer); @@ -3433,8 +3434,6 @@ static int usb_xhci_initfn(struct PCIDevice *dev) xhci->mfwrap_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, xhci_mfwrap_timer, xhci); - xhci->irq = dev->irq[0]; - memory_region_init(&xhci->mem, OBJECT(xhci), "xhci", LEN_REGS); memory_region_init_io(&xhci->mem_cap, OBJECT(xhci), &xhci_cap_ops, xhci, "capabilities", LEN_CAP); diff --git a/hw/usb/host-legacy.c b/hw/usb/host-legacy.c index 3a5f705721..3cc9c4282c 100644 --- a/hw/usb/host-legacy.c +++ b/hw/usb/host-legacy.c @@ -132,7 +132,7 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname) return dev; fail: - qdev_free(&dev->qdev); + object_unparent(OBJECT(dev)); return NULL; } diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 128955dd92..fd320cd8aa 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -137,6 +137,7 @@ static QTAILQ_HEAD(, USBHostDevice) hostdevs = static void usb_host_auto_check(void *unused); static void usb_host_release_interfaces(USBHostDevice *s); static void usb_host_nodev(USBHostDevice *s); +static void usb_host_detach_kernel(USBHostDevice *s); static void usb_host_attach_kernel(USBHostDevice *s); /* ------------------------------------------------------------------------ */ @@ -787,10 +788,13 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev) goto fail; } - libusb_get_device_descriptor(dev, &s->ddesc); s->dev = dev; s->bus_num = bus_num; s->addr = addr; + + usb_host_detach_kernel(s); + + libusb_get_device_descriptor(dev, &s->ddesc); usb_host_get_port(s->dev, s->port, sizeof(s->port)); usb_ep_init(udev); @@ -992,15 +996,14 @@ static int usb_host_claim_interfaces(USBHostDevice *s, int configuration) udev->ninterfaces = 0; udev->configuration = 0; - if (configuration == 0) { - /* address state - ignore */ - return USB_RET_SUCCESS; - } - usb_host_detach_kernel(s); rc = libusb_get_active_config_descriptor(s->dev, &conf); if (rc != 0) { + if (rc == LIBUSB_ERROR_NOT_FOUND) { + /* address state - ignore */ + return USB_RET_SUCCESS; + } return USB_RET_STALL; } @@ -1052,7 +1055,6 @@ static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p) trace_usb_host_set_config(s->bus_num, s->addr, config); usb_host_release_interfaces(s); - usb_host_detach_kernel(s); rc = libusb_set_configuration(s->dh, config); if (rc != 0) { usb_host_libusb_error("libusb_set_configuration", rc); @@ -1256,16 +1258,14 @@ static void usb_host_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) static void usb_host_handle_reset(USBDevice *udev) { USBHostDevice *s = USB_HOST_DEVICE(udev); + int rc; trace_usb_host_reset(s->bus_num, s->addr); - if (udev->configuration == 0) { - return; + rc = libusb_reset_device(s->dh); + if (rc != 0) { + usb_host_nodev(s); } - usb_host_release_interfaces(s); - libusb_reset_device(s->dh); - usb_host_claim_interfaces(s, 0); - usb_host_ep_update(s); } /* diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index 6849a018a9..e6b103c991 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -67,7 +67,6 @@ void virtio_bus_reset(VirtioBusState *bus) /* Destroy the VirtIODevice */ void virtio_bus_destroy_device(VirtioBusState *bus) { - DeviceState *qdev; BusState *qbus = BUS(bus); VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); DPRINTF("%s: remove device.\n", qbus->name); @@ -76,8 +75,7 @@ void virtio_bus_destroy_device(VirtioBusState *bus) if (klass->device_unplug != NULL) { klass->device_unplug(qbus->parent); } - qdev = DEVICE(bus->vdev); - qdev_free(qdev); + object_unparent(OBJECT(bus->vdev)); bus->vdev = NULL; } } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 4825802598..7647be8a3c 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -116,7 +116,7 @@ static void virtio_pci_notify(DeviceState *d, uint16_t vector) if (msix_enabled(&proxy->pci_dev)) msix_notify(&proxy->pci_dev, vector); else - qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1); + pci_set_irq(&proxy->pci_dev, proxy->vdev->isr & 1); } static void virtio_pci_save_config(DeviceState *d, QEMUFile *f) @@ -362,7 +362,7 @@ static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr) /* reading from the ISR also clears it. */ ret = vdev->isr; vdev->isr = 0; - qemu_set_irq(proxy->pci_dev.irq[0], 0); + pci_irq_deassert(&proxy->pci_dev); break; case VIRTIO_MSI_CONFIG_VECTOR: ret = vdev->config_vector; diff --git a/hw/xen/xen_platform.c b/hw/xen/xen_platform.c index 79bf0b33d3..70875e4122 100644 --- a/hw/xen/xen_platform.c +++ b/hw/xen/xen_platform.c @@ -95,7 +95,7 @@ static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) if (pci_get_word(d->config + PCI_CLASS_DEVICE) == PCI_CLASS_NETWORK_ETHERNET && strcmp(d->name, "xen-pci-passthrough") != 0) { - qdev_free(DEVICE(d)); + object_unparent(OBJECT(d)); } } diff --git a/include/block/block_int.h b/include/block/block_int.h index a48731d539..166606615c 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -156,8 +156,11 @@ struct BlockDriver { const char *protocol_name; int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset); + int64_t (*bdrv_getlength)(BlockDriverState *bs); + bool has_variable_length; int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs); + int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); diff --git a/include/block/coroutine.h b/include/block/coroutine.h index 4232569c53..4d5c0cfdd7 100644 --- a/include/block/coroutine.h +++ b/include/block/coroutine.h @@ -216,6 +216,15 @@ void qemu_co_rwlock_unlock(CoRwlock *lock); void coroutine_fn co_sleep_ns(QEMUClockType type, int64_t ns); /** + * Yield the coroutine for a given duration + * + * Behaves similarly to co_sleep_ns(), but the sleeping coroutine will be + * resumed when using qemu_aio_wait(). + */ +void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type, + int64_t ns); + +/** * Yield until a file descriptor becomes readable * * Note that this function clobbers the handlers for the file descriptor. diff --git a/include/elf.h b/include/elf.h index 58bfbf8817..b818091c7b 100644 --- a/include/elf.h +++ b/include/elf.h @@ -1359,6 +1359,9 @@ typedef struct elf64_shdr { #define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ #define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ #define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ /* Note header in a PT_NOTE section */ diff --git a/include/exec/memory.h b/include/exec/memory.h index ebe0d24182..480dfbf9da 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -153,7 +153,7 @@ struct MemoryRegion { bool flush_coalesced_mmio; MemoryRegion *alias; hwaddr alias_offset; - unsigned priority; + int priority; bool may_overlap; QTAILQ_HEAD(subregions, MemoryRegion) subregions; QTAILQ_ENTRY(MemoryRegion) subregions_link; @@ -779,7 +779,7 @@ void memory_region_add_subregion(MemoryRegion *mr, void memory_region_add_subregion_overlap(MemoryRegion *mr, hwaddr offset, MemoryRegion *subregion, - unsigned priority); + int priority); /** * memory_region_get_ram_addr: Get the ram address associated with a memory diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h index 51733d3390..6bbcb1750d 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -165,6 +165,10 @@ extern int acpi_enabled; extern char unsigned *acpi_tables; extern size_t acpi_tables_len; +uint8_t *acpi_table_first(void); +uint8_t *acpi_table_next(uint8_t *current); +unsigned acpi_table_len(void *current); void acpi_table_add(const QemuOpts *opts, Error **errp); +void acpi_table_add_builtin(const QemuOpts *opts, Error **errp); #endif /* !QEMU_HW_ACPI_H */ diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index b1fe71faf5..82fcf9f2eb 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -49,4 +49,6 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base); extern const VMStateDescription vmstate_ich9_pm; +void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp); + #endif /* HW_ACPI_ICH9_H */ diff --git a/include/hw/acpi/piix4.h b/include/hw/acpi/piix4.h new file mode 100644 index 0000000000..65e6fd7aa0 --- /dev/null +++ b/include/hw/acpi/piix4.h @@ -0,0 +1,8 @@ +#ifndef HW_ACPI_PIIX4_H +#define HW_ACPI_PIIX4_H + +#include "qemu/typedefs.h" + +Object *piix4_pm_find(void); + +#endif diff --git a/include/hw/arm/pxa.h b/include/hw/arm/pxa.h index 668232cead..a4e1a66264 100644 --- a/include/hw/arm/pxa.h +++ b/include/hw/arm/pxa.h @@ -97,7 +97,7 @@ typedef struct PXA2xxPCMCIAState PXA2xxPCMCIAState; PXA2xxPCMCIAState *pxa2xx_pcmcia_init(MemoryRegion *sysmem, hwaddr base); int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card); -int pxa2xx_pcmcia_dettach(void *opaque); +int pxa2xx_pcmcia_detach(void *opaque); void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq); /* pxa2xx_keypad.c */ diff --git a/include/hw/cpu/a15mpcore.h b/include/hw/cpu/a15mpcore.h new file mode 100644 index 0000000000..b423533d20 --- /dev/null +++ b/include/hw/cpu/a15mpcore.h @@ -0,0 +1,44 @@ +/* + * Cortex-A15MPCore internal peripheral emulation. + * + * Copyright (c) 2012 Linaro Limited. + * Written by Peter Maydell. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef HW_CPU_A15MPCORE_H +#define HW_CPU_A15MPCORE_H + +#include "hw/sysbus.h" +#include "hw/intc/arm_gic.h" + +/* A15MP private memory region. */ + +#define TYPE_A15MPCORE_PRIV "a15mpcore_priv" +#define A15MPCORE_PRIV(obj) \ + OBJECT_CHECK(A15MPPrivState, (obj), TYPE_A15MPCORE_PRIV) + +typedef struct A15MPPrivState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint32_t num_cpu; + uint32_t num_irq; + MemoryRegion container; + + GICState gic; +} A15MPPrivState; + +#endif diff --git a/include/hw/cpu/a9mpcore.h b/include/hw/cpu/a9mpcore.h new file mode 100644 index 0000000000..010489b98e --- /dev/null +++ b/include/hw/cpu/a9mpcore.h @@ -0,0 +1,37 @@ +/* + * Cortex-A9MPCore internal peripheral emulation. + * + * Copyright (c) 2009 CodeSourcery. + * Copyright (c) 2011 Linaro Limited. + * Written by Paul Brook, Peter Maydell. + * + * This code is licensed under the GPL. + */ +#ifndef HW_CPU_A9MPCORE_H +#define HW_CPU_A9MPCORE_H + +#include "hw/sysbus.h" +#include "hw/intc/arm_gic.h" +#include "hw/misc/a9scu.h" +#include "hw/timer/arm_mptimer.h" + +#define TYPE_A9MPCORE_PRIV "a9mpcore_priv" +#define A9MPCORE_PRIV(obj) \ + OBJECT_CHECK(A9MPPrivState, (obj), TYPE_A9MPCORE_PRIV) + +typedef struct A9MPPrivState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint32_t num_cpu; + MemoryRegion container; + uint32_t num_irq; + + GICState gic; + A9SCUState scu; + ARMMPTimerState mptimer; + ARMMPTimerState wdt; +} A9MPPrivState; + +#endif diff --git a/include/hw/cpu/arm11mpcore.h b/include/hw/cpu/arm11mpcore.h new file mode 100644 index 0000000000..6196109ca2 --- /dev/null +++ b/include/hw/cpu/arm11mpcore.h @@ -0,0 +1,35 @@ +/* + * ARM11MPCore internal peripheral emulation. + * + * Copyright (c) 2006-2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + */ + +#ifndef HW_CPU_ARM11MPCORE_H +#define HW_CPU_ARM11MPCORE_H + +#include "hw/sysbus.h" +#include "hw/misc/arm11scu.h" +#include "hw/intc/arm_gic.h" +#include "hw/timer/arm_mptimer.h" + +#define TYPE_ARM11MPCORE_PRIV "arm11mpcore_priv" +#define ARM11MPCORE_PRIV(obj) \ + OBJECT_CHECK(ARM11MPCorePriveState, (obj), TYPE_ARM11MPCORE_PRIV) + +typedef struct ARM11MPCorePriveState { + SysBusDevice parent_obj; + + uint32_t num_cpu; + MemoryRegion container; + uint32_t num_irq; + + ARM11SCUState scu; + GICState gic; + ARMMPTimerState mptimer; + ARMMPTimerState wdtimer; +} ARM11MPCorePriveState; + +#endif diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index c5f637bffd..4a68b359a6 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -66,6 +66,8 @@ typedef struct ICH9LPCState { qemu_irq *ioapic; } ICH9LPCState; +Object *ich9_lpc_find(void); + #define Q35_MASK(bit, ms_bit, ls_bit) \ ((uint##bit##_t)(((1ULL << ((ms_bit) + 1)) - 1) & ~((1ULL << ls_bit) - 1))) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 6083839084..57e8d16180 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -9,6 +9,9 @@ #include "hw/i386/ioapic.h" #include "qemu/range.h" +#include "qemu/bitmap.h" +#include "sysemu/sysemu.h" +#include "hw/pci/pci.h" /* PC-style peripherals (also used by other machines). */ @@ -17,10 +20,27 @@ typedef struct PcPciInfo { Range w64; } PcPciInfo; +#define ACPI_PM_PROP_S3_DISABLED "disable_s3" +#define ACPI_PM_PROP_S4_DISABLED "disable_s4" +#define ACPI_PM_PROP_S4_VAL "s4_val" +#define ACPI_PM_PROP_SCI_INT "sci_int" +#define ACPI_PM_PROP_ACPI_ENABLE_CMD "acpi_enable_cmd" +#define ACPI_PM_PROP_ACPI_DISABLE_CMD "acpi_disable_cmd" +#define ACPI_PM_PROP_PM_IO_BASE "pm_io_base" +#define ACPI_PM_PROP_GPE0_BLK "gpe0_blk" +#define ACPI_PM_PROP_GPE0_BLK_LEN "gpe0_blk_len" + struct PcGuestInfo { bool has_pci_info; bool isapc_ram_fw; + hwaddr ram_size; + unsigned apic_id_limit; + bool apic_xrupt_override; + uint64_t numa_nodes; + uint64_t *node_mem; + uint64_t *node_cpu; FWCfgState *fw_cfg; + bool has_acpi_build; }; /* parallel.c */ @@ -173,6 +193,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, MemoryRegion *pci_memory, MemoryRegion *ram_memory); +PCIBus *find_i440fx(void); /* piix4.c */ extern PCIDevice *piix4_dev; int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn); @@ -215,6 +236,7 @@ void pc_system_firmware_init(MemoryRegion *rom_memory, /* pvpanic.c */ void pvpanic_init(ISABus *bus); +uint16_t pvpanic_port(void); /* e820 types */ #define E820_RAM 1 @@ -238,6 +260,14 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); .driver = "qemu32-" TYPE_X86_CPU,\ .property = "model",\ .value = stringify(3),\ + },{\ + .driver = "i440FX-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(1),\ + },{\ + .driver = "q35-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(1),\ } #define PC_COMPAT_1_5 \ @@ -274,6 +304,14 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); .driver = TYPE_X86_CPU,\ .property = "pmu",\ .value = "on",\ + },{\ + .driver = "i440FX-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(0),\ + },{\ + .driver = "q35-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(0),\ } #define PC_COMPAT_1_4 \ diff --git a/include/hw/intc/arm_gic.h b/include/hw/intc/arm_gic.h new file mode 100644 index 0000000000..0971e37710 --- /dev/null +++ b/include/hw/intc/arm_gic.h @@ -0,0 +1,42 @@ +/* + * ARM GIC support + * + * Copyright (c) 2012 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_ARM_GIC_H +#define HW_ARM_GIC_H + +#include "arm_gic_common.h" + +#define TYPE_ARM_GIC "arm_gic" +#define ARM_GIC(obj) \ + OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC) +#define ARM_GIC_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICClass, (klass), TYPE_ARM_GIC) +#define ARM_GIC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICClass, (obj), TYPE_ARM_GIC) + +typedef struct ARMGICClass { + /*< private >*/ + ARMGICCommonClass parent_class; + /*< public >*/ + + DeviceRealize parent_realize; +} ARMGICClass; + +#endif diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h new file mode 100644 index 0000000000..4f381bdce7 --- /dev/null +++ b/include/hw/intc/arm_gic_common.h @@ -0,0 +1,92 @@ +/* + * ARM GIC support + * + * Copyright (c) 2012 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef HW_ARM_GIC_COMMON_H +#define HW_ARM_GIC_COMMON_H + +#include "hw/sysbus.h" + +/* Maximum number of possible interrupts, determined by the GIC architecture */ +#define GIC_MAXIRQ 1020 +/* First 32 are private to each CPU (SGIs and PPIs). */ +#define GIC_INTERNAL 32 +/* Maximum number of possible CPU interfaces, determined by GIC architecture */ +#define GIC_NCPU 8 + +typedef struct gic_irq_state { + /* The enable bits are only banked for per-cpu interrupts. */ + uint8_t enabled; + uint8_t pending; + uint8_t active; + uint8_t level; + bool model; /* 0 = N:N, 1 = 1:N */ + bool trigger; /* nonzero = edge triggered. */ +} gic_irq_state; + +typedef struct GICState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + qemu_irq parent_irq[GIC_NCPU]; + bool enabled; + bool cpu_enabled[GIC_NCPU]; + + gic_irq_state irq_state[GIC_MAXIRQ]; + uint8_t irq_target[GIC_MAXIRQ]; + uint8_t priority1[GIC_INTERNAL][GIC_NCPU]; + uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL]; + uint16_t last_active[GIC_MAXIRQ][GIC_NCPU]; + + uint16_t priority_mask[GIC_NCPU]; + uint16_t running_irq[GIC_NCPU]; + uint16_t running_priority[GIC_NCPU]; + uint16_t current_pending[GIC_NCPU]; + + uint32_t num_cpu; + + MemoryRegion iomem; /* Distributor */ + /* This is just so we can have an opaque pointer which identifies + * both this GIC and which CPU interface we should be accessing. + */ + struct GICState *backref[GIC_NCPU]; + MemoryRegion cpuiomem[GIC_NCPU + 1]; /* CPU interfaces */ + uint32_t num_irq; + uint32_t revision; +} GICState; + +#define TYPE_ARM_GIC_COMMON "arm_gic_common" +#define ARM_GIC_COMMON(obj) \ + OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC_COMMON) +#define ARM_GIC_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICCommonClass, (klass), TYPE_ARM_GIC_COMMON) +#define ARM_GIC_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICCommonClass, (obj), TYPE_ARM_GIC_COMMON) + +typedef struct ARMGICCommonClass { + /*< private >*/ + SysBusDeviceClass parent_class; + /*< public >*/ + + void (*pre_save)(GICState *s); + void (*post_load)(GICState *s); +} ARMGICCommonClass; + +#endif diff --git a/include/hw/intc/realview_gic.h b/include/hw/intc/realview_gic.h new file mode 100644 index 0000000000..1783ea11b9 --- /dev/null +++ b/include/hw/intc/realview_gic.h @@ -0,0 +1,28 @@ +/* + * ARM RealView Emulation Baseboard Interrupt Controller + * + * Copyright (c) 2006-2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + */ + +#ifndef HW_INTC_REALVIEW_GIC_H +#define HW_INTC_REALVIEW_GIC_H + +#include "hw/sysbus.h" +#include "hw/intc/arm_gic.h" + +#define TYPE_REALVIEW_GIC "realview_gic" +#define REALVIEW_GIC(obj) \ + OBJECT_CHECK(RealViewGICState, (obj), TYPE_REALVIEW_GIC) + +typedef struct RealViewGICState { + SysBusDevice parent_obj; + + MemoryRegion container; + + GICState gic; +} RealViewGICState; + +#endif diff --git a/include/hw/irq.h b/include/hw/irq.h index 610e6b7623..d08bc02a0d 100644 --- a/include/hw/irq.h +++ b/include/hw/irq.h @@ -30,6 +30,12 @@ static inline void qemu_irq_pulse(qemu_irq irq) */ qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n); +/* + * Allocates a single IRQ. The irq is assigned with a handler, an opaque + * data and the interrupt number. + */ +qemu_irq qemu_allocate_irq(qemu_irq_handler handler, void *opaque, int n); + /* Extends an Array of IRQs. Old IRQs have their handlers and opaque data * preserved. New IRQs are assigned the argument handler and opaque data. */ @@ -37,6 +43,7 @@ qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler, void *opaque, int n); void qemu_free_irqs(qemu_irq *s); +void qemu_free_irq(qemu_irq irq); /* Returns a new IRQ with opposite polarity. */ qemu_irq qemu_irq_invert(qemu_irq irq); diff --git a/include/hw/loader.h b/include/hw/loader.h index 61457360f6..7a23d6bdc1 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -40,11 +40,13 @@ extern bool rom_file_in_ram; int rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex); -int rom_add_blob(const char *name, const void *blob, size_t len, - hwaddr addr); +void *rom_add_blob(const char *name, const void *blob, size_t len, + hwaddr addr, const char *fw_file_name, + FWCfgReadCallback fw_callback, void *callback_opaque); int rom_add_elf_program(const char *name, void *data, size_t datasize, size_t romsize, hwaddr addr); int rom_load_all(void); +void rom_load_done(void); void rom_set_fw(FWCfgState *f); int rom_copy(uint8_t *dest, hwaddr addr, size_t size); void *rom_ptr(hwaddr addr); @@ -53,7 +55,7 @@ void do_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ rom_add_file(_f, NULL, _a, _i) #define rom_add_blob_fixed(_f, _b, _l, _a) \ - rom_add_blob(_f, _b, _l, _a) + rom_add_blob(_f, _b, _l, _a, NULL, NULL, NULL) #define PC_ROM_MIN_VGA 0xc0000 #define PC_ROM_MIN_OPTION 0xc8000 diff --git a/include/hw/misc/a9scu.h b/include/hw/misc/a9scu.h new file mode 100644 index 0000000000..efb0c305c2 --- /dev/null +++ b/include/hw/misc/a9scu.h @@ -0,0 +1,31 @@ +/* + * Cortex-A9MPCore Snoop Control Unit (SCU) emulation. + * + * Copyright (c) 2009 CodeSourcery. + * Copyright (c) 2011 Linaro Limited. + * Written by Paul Brook, Peter Maydell. + * + * This code is licensed under the GPL. + */ +#ifndef HW_MISC_A9SCU_H +#define HW_MISC_A9SCU_H + +#include "hw/sysbus.h" + +/* A9MP private memory region. */ + +typedef struct A9SCUState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + uint32_t control; + uint32_t status; + uint32_t num_cpu; +} A9SCUState; + +#define TYPE_A9_SCU "a9-scu" +#define A9_SCU(obj) OBJECT_CHECK(A9SCUState, (obj), TYPE_A9_SCU) + +#endif diff --git a/include/hw/misc/arm11scu.h b/include/hw/misc/arm11scu.h new file mode 100644 index 0000000000..5ad0f3d339 --- /dev/null +++ b/include/hw/misc/arm11scu.h @@ -0,0 +1,29 @@ +/* + * ARM11MPCore Snoop Control Unit (SCU) emulation + * + * Copyright (c) 2006-2007 CodeSourcery. + * Copyright (c) 2013 SUSE LINUX Products GmbH + * Written by Paul Brook and Andreas Färber + * + * This code is licensed under the GPL. + */ + +#ifndef HW_MISC_ARM11SCU_H +#define HW_MISC_ARM11SCU_H + +#include "hw/sysbus.h" + +#define TYPE_ARM11_SCU "arm11-scu" +#define ARM11_SCU(obj) OBJECT_CHECK(ARM11SCUState, (obj), TYPE_ARM11_SCU) + +typedef struct ARM11SCUState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint32_t control; + uint32_t num_cpu; + MemoryRegion iomem; +} ARM11SCUState; + +#endif diff --git a/include/hw/misc/arm_integrator_debug.h b/include/hw/misc/arm_integrator_debug.h new file mode 100644 index 0000000000..37789b69d9 --- /dev/null +++ b/include/hw/misc/arm_integrator_debug.h @@ -0,0 +1,18 @@ +/* + * ARM Integrator Board Debug, switch and LED section + * + * Browse the data sheet: + * + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0159b/Babbfijf.html + * + * Copyright (c) 2013 Alex Bennée <alex@bennee.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_INTEGRATOR_DEBUG_H +#define QEMU_INTEGRATOR_DEBUG_H + +#define TYPE_INTEGRATOR_DEBUG "integrator_debug" + +#endif diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index f60dd676c8..72b1549dc4 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -46,12 +46,14 @@ #define FW_CFG_INVALID 0xffff +#define FW_CFG_MAX_FILE_PATH 56 + #ifndef NO_QEMU_PROTOS typedef struct FWCfgFile { uint32_t size; /* file size */ uint16_t select; /* write this to 0x510 to read it */ uint16_t reserved; - char name[56]; + char name[FW_CFG_MAX_FILE_PATH]; } FWCfgFile; typedef struct FWCfgFiles { @@ -60,6 +62,7 @@ typedef struct FWCfgFiles { } FWCfgFiles; typedef void (*FWCfgCallback)(void *opaque, uint8_t *data); +typedef void (*FWCfgReadCallback)(void *opaque, uint32_t offset); void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len); void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value); @@ -70,6 +73,9 @@ void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, void *callback_opaque, void *data, size_t len); void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data, size_t len); +void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, + FWCfgReadCallback callback, void *callback_opaque, + void *data, size_t len); FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, hwaddr crl_addr, hwaddr data_addr); diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index 56de92ede2..309065fa41 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -61,6 +61,7 @@ typedef struct MCHPCIState { ram_addr_t above_4g_mem_size; uint64_t pci_hole64_size; PcGuestInfo *guest_info; + uint32_t short_root_bus; } MCHPCIState; typedef struct Q35PCIHost { @@ -156,4 +157,6 @@ typedef struct Q35PCIHost { #define MCH_PCIE_DEV 1 #define MCH_PCIE_FUNC 0 +uint64_t mch_mcfg_base(void); + #endif /* HW_Q35_H */ diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 4b90e5d00b..b783e68d08 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -247,9 +247,6 @@ struct PCIDevice { PCIConfigReadFunc *config_read; PCIConfigWriteFunc *config_write; - /* IRQ objects for the INTA-INTD pins. */ - qemu_irq *irq; - /* Legacy PCI VGA regions */ MemoryRegion *vga_regions[QEMU_PCI_VGA_NUM_REGIONS]; bool has_vga; @@ -424,25 +421,25 @@ pci_get_byte(const uint8_t *config) static inline void pci_set_word(uint8_t *config, uint16_t val) { - cpu_to_le16wu((uint16_t *)config, val); + stw_le_p(config, val); } static inline uint16_t pci_get_word(const uint8_t *config) { - return le16_to_cpupu((const uint16_t *)config); + return lduw_le_p(config); } static inline void pci_set_long(uint8_t *config, uint32_t val) { - cpu_to_le32wu((uint32_t *)config, val); + stl_le_p(config, val); } static inline uint32_t pci_get_long(const uint8_t *config) { - return le32_to_cpupu((const uint32_t *)config); + return ldl_le_p(config); } static inline void @@ -632,6 +629,29 @@ PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name); PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name); +qemu_irq pci_allocate_irq(PCIDevice *pci_dev); +void pci_set_irq(PCIDevice *pci_dev, int level); + +static inline void pci_irq_assert(PCIDevice *pci_dev) +{ + pci_set_irq(pci_dev, 1); +} + +static inline void pci_irq_deassert(PCIDevice *pci_dev) +{ + pci_set_irq(pci_dev, 0); +} + +/* + * FIXME: PCI does not work this way. + * All the callers to this method should be fixed. + */ +static inline void pci_irq_pulse(PCIDevice *pci_dev) +{ + pci_irq_assert(pci_dev); + pci_irq_deassert(pci_dev); +} + static inline int pci_is_express(const PCIDevice *d) { return d->cap_present & QEMU_PCI_CAP_EXPRESS; diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index c010007c5e..1966169553 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -64,15 +64,6 @@ struct PCIExpressDevice { uint8_t exp_cap; /* SLOT */ - unsigned int hpev_intx; /* INTx for hot plug event (0-3:INT[A-D]#) - * default is 0 = INTA# - * If the chip wants to use other interrupt - * line, initialize this member with the - * desired number. - * If the chip dynamically changes this member, - * also initialize it when loaded as - * appropreately. - */ bool hpev_notified; /* Logical AND of conditions for hot plug event. Following 6.7.3.4: Software Notification of Hot-Plug Events, an interrupt @@ -82,15 +73,6 @@ struct PCIExpressDevice { /* AER */ uint16_t aer_cap; PCIEAERLog aer_log; - unsigned int aer_intx; /* INTx for error reporting - * default is 0 = INTA# - * If the chip wants to use other interrupt - * line, initialize this member with the - * desired number. - * If the chip dynamically changes this member, - * also initialize it when loaded as - * appropreately. - */ }; /* PCI express capability helper functions */ diff --git a/include/hw/pci/pcie_host.h b/include/hw/pci/pcie_host.h index 1228e36cb2..acca45ed58 100644 --- a/include/hw/pci/pcie_host.h +++ b/include/hw/pci/pcie_host.h @@ -28,6 +28,12 @@ #define PCIE_HOST_BRIDGE(obj) \ OBJECT_CHECK(PCIExpressHost, (obj), TYPE_PCIE_HOST_BRIDGE) +#define PCIE_HOST_MCFG_BASE "MCFG" +#define PCIE_HOST_MCFG_SIZE "mcfg_size" + +/* pcie_host::base_addr == PCIE_BASE_ADDR_UNMAPPED when it isn't mapped. */ +#define PCIE_BASE_ADDR_UNMAPPED ((hwaddr)-1ULL) + struct PCIExpressHost { PCIHostState pci; @@ -51,4 +57,25 @@ void pcie_host_mmcfg_update(PCIExpressHost *e, hwaddr addr, uint32_t size); +/* + * PCI express ECAM (Enhanced Configuration Address Mapping) format. + * AKA mmcfg address + * bit 20 - 28: bus number + * bit 15 - 19: device number + * bit 12 - 14: function number + * bit 0 - 11: offset in configuration space of a given device + */ +#define PCIE_MMCFG_SIZE_MAX (1ULL << 28) +#define PCIE_MMCFG_SIZE_MIN (1ULL << 20) +#define PCIE_MMCFG_BUS_BIT 20 +#define PCIE_MMCFG_BUS_MASK 0x1ff +#define PCIE_MMCFG_DEVFN_BIT 12 +#define PCIE_MMCFG_DEVFN_MASK 0xff +#define PCIE_MMCFG_CONFOFFSET_MASK 0xfff +#define PCIE_MMCFG_BUS(addr) (((addr) >> PCIE_MMCFG_BUS_BIT) & \ + PCIE_MMCFG_BUS_MASK) +#define PCIE_MMCFG_DEVFN(addr) (((addr) >> PCIE_MMCFG_DEVFN_BIT) & \ + PCIE_MMCFG_DEVFN_MASK) +#define PCIE_MMCFG_CONFOFFSET(addr) ((addr) & PCIE_MMCFG_CONFOFFSET_MASK) + #endif /* PCIE_HOST_H */ diff --git a/include/hw/pcmcia.h b/include/hw/pcmcia.h index f91669305e..2695d3cba6 100644 --- a/include/hw/pcmcia.h +++ b/include/hw/pcmcia.h @@ -3,11 +3,11 @@ /* PCMCIA/Cardbus */ -#include "qemu-common.h" +#include "hw/qdev.h" -typedef struct { +typedef struct PCMCIASocket { qemu_irq irq; - int attached; + bool attached; const char *slot_string; const char *card_string; } PCMCIASocket; @@ -16,22 +16,42 @@ void pcmcia_socket_register(PCMCIASocket *socket); void pcmcia_socket_unregister(PCMCIASocket *socket); void pcmcia_info(Monitor *mon, const QDict *qdict); +#define TYPE_PCMCIA_CARD "pcmcia-card" +#define PCMCIA_CARD(obj) \ + OBJECT_CHECK(PCMCIACardState, (obj), TYPE_PCMCIA_CARD) +#define PCMCIA_CARD_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PCMCIACardClass, obj, TYPE_PCMCIA_CARD) +#define PCMCIA_CARD_CLASS(cls) \ + OBJECT_CLASS_CHECK(PCMCIACardClass, cls, TYPE_PCMCIA_CARD) + struct PCMCIACardState { - void *state; + /*< private >*/ + DeviceState parent_obj; + /*< public >*/ + PCMCIASocket *slot; - int (*attach)(void *state); - int (*detach)(void *state); +}; + +typedef struct PCMCIACardClass { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + + int (*attach)(PCMCIACardState *state); + int (*detach)(PCMCIACardState *state); + const uint8_t *cis; int cis_len; /* Only valid if attached */ - uint8_t (*attr_read)(void *state, uint32_t address); - void (*attr_write)(void *state, uint32_t address, uint8_t value); - uint16_t (*common_read)(void *state, uint32_t address); - void (*common_write)(void *state, uint32_t address, uint16_t value); - uint16_t (*io_read)(void *state, uint32_t address); - void (*io_write)(void *state, uint32_t address, uint16_t value); -}; + uint8_t (*attr_read)(PCMCIACardState *card, uint32_t address); + void (*attr_write)(PCMCIACardState *card, uint32_t address, uint8_t value); + uint16_t (*common_read)(PCMCIACardState *card, uint32_t address); + void (*common_write)(PCMCIACardState *card, + uint32_t address, uint16_t value); + uint16_t (*io_read)(PCMCIACardState *card, uint32_t address); + void (*io_write)(PCMCIACardState *card, uint32_t address, uint16_t value); +} PCMCIACardClass; #define CISTPL_DEVICE 0x01 /* 5V Device Information Tuple */ #define CISTPL_NO_LINK 0x14 /* No Link Tuple */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index e37b41983c..fdaab2de52 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -29,7 +29,6 @@ typedef struct sPAPREnvironment { target_ulong entry_point; uint32_t next_irq; uint64_t rtc_offset; - char *cpu_model; bool has_graphics; uint32_t epow_irq; @@ -283,6 +282,7 @@ typedef struct sPAPREnvironment { #define H_GET_EM_PARMS 0x2B8 #define H_SET_MPP 0x2D0 #define H_GET_MPP 0x2D4 +#define H_XIRR_X 0x2FC #define H_SET_MODE 0x31C #define MAX_HCALL_OPCODE H_SET_MODE @@ -332,14 +332,19 @@ static inline int spapr_allocate_lsi(int hint) return spapr_allocate_irq(hint, true); } +static inline uint64_t ppc64_phys_to_real(uint64_t addr) +{ + return addr & ~0xF000000000000000ULL; +} + static inline uint32_t rtas_ld(target_ulong phys, int n) { - return ldl_be_phys(phys + 4*n); + return ldl_be_phys(ppc64_phys_to_real(phys + 4*n)); } static inline void rtas_st(target_ulong phys, int n, uint32_t val) { - stl_be_phys(phys + 4*n, val); + stl_be_phys(ppc64_phys_to_real(phys + 4*n), val); } typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, sPAPREnvironment *spapr, diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 66364c5faf..0d7673de94 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -29,9 +29,24 @@ #include "hw/sysbus.h" +#define TYPE_XICS_COMMON "xics-common" +#define XICS_COMMON(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_COMMON) + #define TYPE_XICS "xics" #define XICS(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS) +#define TYPE_KVM_XICS "xics-kvm" +#define KVM_XICS(obj) OBJECT_CHECK(KVMXICSState, (obj), TYPE_KVM_XICS) + +#define XICS_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_COMMON) +#define XICS_CLASS(klass) \ + OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS) +#define XICS_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_COMMON) +#define XICS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS) + #define XICS_IPI 0x2 #define XICS_BUID 0x1 #define XICS_IRQ_BASE (XICS_BUID << 12) @@ -41,11 +56,22 @@ * (the kernel implementation supports more but we don't exploit * that yet) */ +typedef struct XICSStateClass XICSStateClass; typedef struct XICSState XICSState; +typedef struct ICPStateClass ICPStateClass; typedef struct ICPState ICPState; +typedef struct ICSStateClass ICSStateClass; typedef struct ICSState ICSState; typedef struct ICSIRQState ICSIRQState; +struct XICSStateClass { + DeviceClass parent_class; + + void (*cpu_setup)(XICSState *icp, PowerPCCPU *cpu); + void (*set_nr_irqs)(XICSState *icp, uint32_t nr_irqs, Error **errp); + void (*set_nr_servers)(XICSState *icp, uint32_t nr_servers, Error **errp); +}; + struct XICSState { /*< private >*/ SysBusDevice parent_obj; @@ -59,10 +85,26 @@ struct XICSState { #define TYPE_ICP "icp" #define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP) +#define TYPE_KVM_ICP "icp-kvm" +#define KVM_ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_KVM_ICP) + +#define ICP_CLASS(klass) \ + OBJECT_CLASS_CHECK(ICPStateClass, (klass), TYPE_ICP) +#define ICP_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ICPStateClass, (obj), TYPE_ICP) + +struct ICPStateClass { + DeviceClass parent_class; + + void (*pre_save)(ICPState *s); + int (*post_load)(ICPState *s, int version_id); +}; + struct ICPState { /*< private >*/ DeviceState parent_obj; /*< public >*/ + CPUState *cs; uint32_t xirr; uint8_t pending_priority; uint8_t mfrr; @@ -72,6 +114,21 @@ struct ICPState { #define TYPE_ICS "ics" #define ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS) +#define TYPE_KVM_ICS "icskvm" +#define KVM_ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_KVM_ICS) + +#define ICS_CLASS(klass) \ + OBJECT_CLASS_CHECK(ICSStateClass, (klass), TYPE_ICS) +#define ICS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ICSStateClass, (obj), TYPE_ICS) + +struct ICSStateClass { + DeviceClass parent_class; + + void (*pre_save)(ICSState *s); + int (*post_load)(ICSState *s, int version_id); +}; + struct ICSState { /*< private >*/ DeviceState parent_obj; diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index e191ca0bd2..f2043a69c2 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -221,7 +221,6 @@ void qdev_init_nofail(DeviceState *dev); void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version); void qdev_unplug(DeviceState *dev, Error **errp); -void qdev_free(DeviceState *dev); int qdev_simple_unplug_cb(DeviceState *dev); void qdev_machine_creation_done(void); bool qdev_machine_modified(void); diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index bb50a877cc..f5aaa05ee3 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -68,7 +68,7 @@ void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size); void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq); void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr); void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, - unsigned priority); + int priority); void sysbus_add_io(SysBusDevice *dev, hwaddr addr, MemoryRegion *mem); void sysbus_del_io(SysBusDevice *dev, MemoryRegion *mem); diff --git a/include/hw/timer/arm_mptimer.h b/include/hw/timer/arm_mptimer.h new file mode 100644 index 0000000000..b34cba00ce --- /dev/null +++ b/include/hw/timer/arm_mptimer.h @@ -0,0 +1,54 @@ +/* + * Private peripheral timer/watchdog blocks for ARM 11MPCore and A9MP + * + * Copyright (c) 2006-2007 CodeSourcery. + * Copyright (c) 2011 Linaro Limited + * Written by Paul Brook, Peter Maydell + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef HW_TIMER_ARM_MPTIMER_H +#define HW_TIMER_ARM_MPTIMER_H + +#include "hw/sysbus.h" + +#define ARM_MPTIMER_MAX_CPUS 4 + +/* State of a single timer or watchdog block */ +typedef struct { + uint32_t count; + uint32_t load; + uint32_t control; + uint32_t status; + int64_t tick; + QEMUTimer *timer; + qemu_irq irq; + MemoryRegion iomem; +} TimerBlock; + +#define TYPE_ARM_MPTIMER "arm_mptimer" +#define ARM_MPTIMER(obj) \ + OBJECT_CHECK(ARMMPTimerState, (obj), TYPE_ARM_MPTIMER) + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + uint32_t num_cpu; + TimerBlock timerblock[ARM_MPTIMER_MAX_CPUS]; + MemoryRegion iomem; +} ARMMPTimerState; + +#endif diff --git a/include/hw/timer/hpet.h b/include/hw/timer/hpet.h index 757f79fdd2..ab44bd31fd 100644 --- a/include/hw/timer/hpet.h +++ b/include/hw/timer/hpet.h @@ -71,4 +71,6 @@ struct hpet_fw_config } QEMU_PACKED; extern struct hpet_fw_config hpet_cfg; + +bool hpet_find(void); #endif diff --git a/include/net/eth.h b/include/net/eth.h index 1d48e06b22..b3273b823f 100644 --- a/include/net/eth.h +++ b/include/net/eth.h @@ -84,7 +84,7 @@ typedef struct ip_pseudo_header { } ip_pseudo_header; /* IPv6 address */ -struct in6_addr { +struct in6_address { union { uint8_t __u6_addr8[16]; } __in6_u; @@ -105,8 +105,8 @@ struct ip6_header { uint8_t ip6_un3_ecn; /* 2 bits ECN, top 6 bits payload length */ } ip6_un3; } ip6_ctlun; - struct in6_addr ip6_src; /* source address */ - struct in6_addr ip6_dst; /* destination address */ + struct in6_address ip6_src; /* source address */ + struct in6_address ip6_dst; /* destination address */ }; struct ip6_ext_hdr { diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 14a5f657ce..437b8e0a9e 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -410,53 +410,6 @@ static inline void stfq_be_p(void *ptr, float64 v) stq_be_p(ptr, u.ll); } -/* Legacy unaligned versions. Note that we never had a complete set. */ - -static inline void cpu_to_le16wu(uint16_t *p, uint16_t v) -{ - stw_le_p(p, v); -} - -static inline void cpu_to_le32wu(uint32_t *p, uint32_t v) -{ - stl_le_p(p, v); -} - -static inline uint16_t le16_to_cpupu(const uint16_t *p) -{ - return lduw_le_p(p); -} - -static inline uint32_t le32_to_cpupu(const uint32_t *p) -{ - return ldl_le_p(p); -} - -static inline uint32_t be32_to_cpupu(const uint32_t *p) -{ - return ldl_be_p(p); -} - -static inline void cpu_to_be16wu(uint16_t *p, uint16_t v) -{ - stw_be_p(p, v); -} - -static inline void cpu_to_be32wu(uint32_t *p, uint32_t v) -{ - stl_be_p(p, v); -} - -static inline void cpu_to_be64wu(uint64_t *p, uint64_t v) -{ - stq_be_p(p, v); -} - -static inline void cpu_to_32wu(uint32_t *p, uint32_t v) -{ - stl_p(p, v); -} - static inline unsigned long leul_to_cpu(unsigned long v) { /* In order to break an include loop between here and diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h index ad4a9e5c3a..508428ff32 100644 --- a/include/qemu/config-file.h +++ b/include/qemu/config-file.h @@ -8,6 +8,7 @@ QemuOptsList *qemu_find_opts(const char *group); QemuOptsList *qemu_find_opts_err(const char *group, Error **errp); void qemu_add_opts(QemuOptsList *list); +void qemu_add_drive_opts(QemuOptsList *list); int qemu_set_option(const char *str); int qemu_global_option(const char *str); void qemu_add_globals(void); diff --git a/include/qom/object.h b/include/qom/object.h index 1a7b71aba5..a275db2092 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -18,9 +18,9 @@ #include <stdint.h> #include <stdbool.h> #include "qemu/queue.h" +#include "qapi/error.h" struct Visitor; -struct Error; struct TypeImpl; typedef struct TypeImpl *Type; @@ -301,7 +301,7 @@ typedef void (ObjectPropertyAccessor)(Object *obj, struct Visitor *v, void *opaque, const char *name, - struct Error **errp); + Error **errp); /** * ObjectPropertyRelease: @@ -790,9 +790,9 @@ void object_property_add(Object *obj, const char *name, const char *type, ObjectPropertyAccessor *get, ObjectPropertyAccessor *set, ObjectPropertyRelease *release, - void *opaque, struct Error **errp); + void *opaque, Error **errp); -void object_property_del(Object *obj, const char *name, struct Error **errp); +void object_property_del(Object *obj, const char *name, Error **errp); /** * object_property_find: @@ -803,7 +803,7 @@ void object_property_del(Object *obj, const char *name, struct Error **errp); * Look up a property for an object and return its #ObjectProperty if found. */ ObjectProperty *object_property_find(Object *obj, const char *name, - struct Error **errp); + Error **errp); void object_unparent(Object *obj); @@ -818,7 +818,7 @@ void object_unparent(Object *obj); * Reads a property from a object. */ void object_property_get(Object *obj, struct Visitor *v, const char *name, - struct Error **errp); + Error **errp); /** * object_property_set_str: @@ -829,7 +829,7 @@ void object_property_get(Object *obj, struct Visitor *v, const char *name, * Writes a string value to a property. */ void object_property_set_str(Object *obj, const char *value, - const char *name, struct Error **errp); + const char *name, Error **errp); /** * object_property_get_str: @@ -842,7 +842,7 @@ void object_property_set_str(Object *obj, const char *value, * The caller should free the string. */ char *object_property_get_str(Object *obj, const char *name, - struct Error **errp); + Error **errp); /** * object_property_set_link: @@ -853,7 +853,7 @@ char *object_property_get_str(Object *obj, const char *name, * Writes an object's canonical path to a property. */ void object_property_set_link(Object *obj, Object *value, - const char *name, struct Error **errp); + const char *name, Error **errp); /** * object_property_get_link: @@ -866,7 +866,7 @@ void object_property_set_link(Object *obj, Object *value, * string or not a valid object path). */ Object *object_property_get_link(Object *obj, const char *name, - struct Error **errp); + Error **errp); /** * object_property_set_bool: @@ -877,7 +877,7 @@ Object *object_property_get_link(Object *obj, const char *name, * Writes a bool value to a property. */ void object_property_set_bool(Object *obj, bool value, - const char *name, struct Error **errp); + const char *name, Error **errp); /** * object_property_get_bool: @@ -889,7 +889,7 @@ void object_property_set_bool(Object *obj, bool value, * an error occurs (including when the property value is not a bool). */ bool object_property_get_bool(Object *obj, const char *name, - struct Error **errp); + Error **errp); /** * object_property_set_int: @@ -900,7 +900,7 @@ bool object_property_get_bool(Object *obj, const char *name, * Writes an integer value to a property. */ void object_property_set_int(Object *obj, int64_t value, - const char *name, struct Error **errp); + const char *name, Error **errp); /** * object_property_get_int: @@ -912,7 +912,7 @@ void object_property_set_int(Object *obj, int64_t value, * an error occurs (including when the property value is not an integer). */ int64_t object_property_get_int(Object *obj, const char *name, - struct Error **errp); + Error **errp); /** * object_property_set: @@ -926,7 +926,7 @@ int64_t object_property_get_int(Object *obj, const char *name, * Writes a property to a object. */ void object_property_set(Object *obj, struct Visitor *v, const char *name, - struct Error **errp); + Error **errp); /** * object_property_parse: @@ -938,7 +938,7 @@ void object_property_set(Object *obj, struct Visitor *v, const char *name, * Parses a string and writes the result into a property of an object. */ void object_property_parse(Object *obj, const char *string, - const char *name, struct Error **errp); + const char *name, Error **errp); /** * object_property_print: @@ -950,7 +950,7 @@ void object_property_parse(Object *obj, const char *string, * caller shall free the string. */ char *object_property_print(Object *obj, const char *name, - struct Error **errp); + Error **errp); /** * object_property_get_type: @@ -961,7 +961,7 @@ char *object_property_print(Object *obj, const char *name, * Returns: The type name of the property. */ const char *object_property_get_type(Object *obj, const char *name, - struct Error **errp); + Error **errp); /** * object_get_root: @@ -1054,7 +1054,7 @@ Object *object_resolve_path_component(Object *parent, const gchar *part); * The child object itself can be retrieved using object_property_get_link(). */ void object_property_add_child(Object *obj, const char *name, - Object *child, struct Error **errp); + Object *child, Error **errp); /** * object_property_add_link: @@ -1077,7 +1077,7 @@ void object_property_add_child(Object *obj, const char *name, */ void object_property_add_link(Object *obj, const char *name, const char *type, Object **child, - struct Error **errp); + Error **errp); /** * object_property_add_str: @@ -1092,9 +1092,9 @@ void object_property_add_link(Object *obj, const char *name, * property of type 'string'. */ void object_property_add_str(Object *obj, const char *name, - char *(*get)(Object *, struct Error **), - void (*set)(Object *, const char *, struct Error **), - struct Error **errp); + char *(*get)(Object *, Error **), + void (*set)(Object *, const char *, Error **), + Error **errp); /** * object_property_add_bool: @@ -1108,9 +1108,61 @@ void object_property_add_str(Object *obj, const char *name, * property of type 'bool'. */ void object_property_add_bool(Object *obj, const char *name, - bool (*get)(Object *, struct Error **), - void (*set)(Object *, bool, struct Error **), - struct Error **errp); + bool (*get)(Object *, Error **), + void (*set)(Object *, bool, Error **), + Error **errp); + +/** + * object_property_add_uint8_ptr: + * @obj: the object to add a property to + * @name: the name of the property + * @v: pointer to value + * @errp: if an error occurs, a pointer to an area to store the error + * + * Add an integer property in memory. This function will add a + * property of type 'uint8'. + */ +void object_property_add_uint8_ptr(Object *obj, const char *name, + const uint8_t *v, Error **errp); + +/** + * object_property_add_uint16_ptr: + * @obj: the object to add a property to + * @name: the name of the property + * @v: pointer to value + * @errp: if an error occurs, a pointer to an area to store the error + * + * Add an integer property in memory. This function will add a + * property of type 'uint16'. + */ +void object_property_add_uint16_ptr(Object *obj, const char *name, + const uint16_t *v, Error **errp); + +/** + * object_property_add_uint32_ptr: + * @obj: the object to add a property to + * @name: the name of the property + * @v: pointer to value + * @errp: if an error occurs, a pointer to an area to store the error + * + * Add an integer property in memory. This function will add a + * property of type 'uint32'. + */ +void object_property_add_uint32_ptr(Object *obj, const char *name, + const uint32_t *v, Error **errp); + +/** + * object_property_add_uint64_ptr: + * @obj: the object to add a property to + * @name: the name of the property + * @v: pointer to value + * @errp: if an error occurs, a pointer to an area to store the error + * + * Add an integer property in memory. This function will add a + * property of type 'uint64'. + */ +void object_property_add_uint64_ptr(Object *obj, const char *name, + const uint64_t *v, Error **Errp); /** * object_child_foreach: diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index cd5791eb74..495dae8c39 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -193,6 +193,8 @@ QemuOpts *qemu_get_machine_opts(void); bool usb_enabled(bool default_usb); +extern QemuOptsList qemu_legacy_drive_opts; +extern QemuOptsList qemu_common_drive_opts; extern QemuOptsList qemu_drive_opts; extern QemuOptsList qemu_chardev_opts; extern QemuOptsList qemu_device_opts; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 4a14a43037..eaaf00ddd0 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -4870,10 +4870,10 @@ static inline abi_long host_to_target_stat64(void *cpu_env, } else #endif { -#if TARGET_ABI_BITS == 64 && !defined(TARGET_ALPHA) - struct target_stat *target_st; -#else +#if defined(TARGET_HAS_STRUCT_STAT64) struct target_stat64 *target_st; +#else + struct target_stat *target_st; #endif if (!lock_user_struct(VERIFY_WRITE, target_st, target_addr, 0)) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 5f53a28d1b..fe540f6563 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -1178,6 +1178,7 @@ struct target_stat { /* This matches struct stat64 in glibc2.1, hence the absolutely * insane amounts of padding around dev_t's. */ +#define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { unsigned short st_dev; unsigned char __pad0[10]; @@ -1213,6 +1214,7 @@ struct target_stat64 { } QEMU_PACKED; #ifdef TARGET_ARM +#define TARGET_HAS_STRUCT_STAT64 struct target_eabi_stat64 { unsigned long long st_dev; unsigned int __pad1; @@ -1262,6 +1264,7 @@ struct target_stat { abi_ulong __unused4[2]; }; +#define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { unsigned char __pad0[6]; unsigned short st_dev; @@ -1317,6 +1320,7 @@ struct target_stat { abi_ulong __unused4[2]; }; +#define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { unsigned char __pad0[6]; unsigned short st_dev; @@ -1384,6 +1388,8 @@ struct target_stat { #endif }; +#if !defined(TARGET_PPC64) || defined(TARGET_ABI32) +#define TARGET_HAS_STRUCT_STAT64 struct QEMU_PACKED target_stat64 { unsigned long long st_dev; unsigned long long st_ino; @@ -1406,6 +1412,7 @@ struct QEMU_PACKED target_stat64 { unsigned int __unused4; unsigned int __unused5; }; +#endif #elif defined(TARGET_MICROBLAZE) @@ -1431,6 +1438,7 @@ struct target_stat { }; /* FIXME: Microblaze no-mmu user-space has a difference stat64 layout... */ +#define TARGET_HAS_STRUCT_STAT64 struct QEMU_PACKED target_stat64 { uint64_t st_dev; #define TARGET_STAT64_HAS_BROKEN_ST_INO 1 @@ -1486,6 +1494,7 @@ struct target_stat { /* This matches struct stat64 in glibc2.1, hence the absolutely * insane amounts of padding around dev_t's. */ +#define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { unsigned long long st_dev; unsigned char __pad1[2]; @@ -1594,6 +1603,7 @@ struct target_stat { * struct stat of the 64-bit kernel. */ +#define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { unsigned int st_dev; unsigned int st_pad0[3]; /* Reserved for st_dev expansion */ @@ -1665,6 +1675,7 @@ struct target_stat { * struct stat of the 64-bit kernel. */ +#define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { abi_ulong st_dev; abi_ulong st_pad0[3]; /* Reserved for st_dev expansion */ @@ -1721,6 +1732,7 @@ struct target_stat { unsigned int st_gen; }; +#define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { abi_ulong st_dev; abi_ulong st_ino; @@ -1770,6 +1782,7 @@ struct target_stat { /* This matches struct stat64 in glibc2.1, hence the absolutely * insane amounts of padding around dev_t's. */ +#define TARGET_HAS_STRUCT_STAT64 struct QEMU_PACKED target_stat64 { unsigned long long st_dev; unsigned char __pad0[4]; @@ -1897,6 +1910,7 @@ struct target_stat { unsigned int __unused5; }; +#define TARGET_HAS_STRUCT_STAT64 struct target_stat64 { uint64_t st_dev; uint64_t st_ino; diff --git a/memory.c b/memory.c index 7f1f2661a5..28f64491d0 100644 --- a/memory.c +++ b/memory.c @@ -1473,7 +1473,7 @@ void memory_region_add_subregion(MemoryRegion *mr, void memory_region_add_subregion_overlap(MemoryRegion *mr, hwaddr offset, MemoryRegion *subregion, - unsigned priority) + int priority) { subregion->may_overlap = true; subregion->priority = priority; @@ -1506,7 +1506,7 @@ void memory_region_set_enabled(MemoryRegion *mr, bool enabled) void memory_region_set_address(MemoryRegion *mr, hwaddr addr) { MemoryRegion *parent = mr->parent; - unsigned priority = mr->priority; + int priority = mr->priority; bool may_overlap = mr->may_overlap; if (addr == mr->addr || !parent) { diff --git a/monitor.c b/monitor.c index 74f3f1ba60..845f608665 100644 --- a/monitor.c +++ b/monitor.c @@ -511,7 +511,6 @@ static const char *monitor_event_names[] = { QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX) MonitorEventState monitor_event_state[QEVENT_MAX]; -QemuMutex monitor_event_state_lock; /* * Emits the event to every monitor instance @@ -543,7 +542,6 @@ monitor_protocol_event_queue(MonitorEvent event, int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); assert(event < QEVENT_MAX); - qemu_mutex_lock(&monitor_event_state_lock); evstate = &(monitor_event_state[event]); trace_monitor_protocol_event_queue(event, data, @@ -576,7 +574,6 @@ monitor_protocol_event_queue(MonitorEvent event, evstate->last = now; } } - qemu_mutex_unlock(&monitor_event_state_lock); } @@ -589,7 +586,6 @@ static void monitor_protocol_event_handler(void *opaque) MonitorEventState *evstate = opaque; int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - qemu_mutex_lock(&monitor_event_state_lock); trace_monitor_protocol_event_handler(evstate->event, evstate->data, @@ -601,7 +597,6 @@ static void monitor_protocol_event_handler(void *opaque) evstate->data = NULL; } evstate->last = now; - qemu_mutex_unlock(&monitor_event_state_lock); } @@ -638,7 +633,6 @@ monitor_protocol_event_throttle(MonitorEvent event, * and initialize state */ static void monitor_protocol_event_init(void) { - qemu_mutex_init(&monitor_event_state_lock); /* Limit RTC & BALLOON events to 1 per second */ monitor_protocol_event_throttle(QEVENT_RTC_CHANGE, 1000); monitor_protocol_event_throttle(QEVENT_BALLOON_CHANGE, 1000); @@ -3186,6 +3180,9 @@ static const MonitorDef monitor_defs[] = { { "srr0", offsetof(CPUPPCState, spr[SPR_SRR0]) }, { "srr1", offsetof(CPUPPCState, spr[SPR_SRR1]) }, + { "dar", offsetof(CPUPPCState, spr[SPR_DAR]) }, + { "dsisr", offsetof(CPUPPCState, spr[SPR_DSISR]) }, + { "cfar", offsetof(CPUPPCState, spr[SPR_CFAR]) }, { "sprg0", offsetof(CPUPPCState, spr[SPR_SPRG0]) }, { "sprg1", offsetof(CPUPPCState, spr[SPR_SPRG1]) }, { "sprg2", offsetof(CPUPPCState, spr[SPR_SPRG2]) }, diff --git a/net/net.c b/net/net.c index c330c9a3a8..0a88e68253 100644 --- a/net/net.c +++ b/net/net.c @@ -27,6 +27,7 @@ #include "clients.h" #include "hub.h" #include "net/slirp.h" +#include "net/eth.h" #include "util.h" #include "monitor/monitor.h" @@ -442,7 +443,6 @@ void qemu_flush_queued_packets(NetClientState *nc) if (net_hub_flush(nc->peer)) { qemu_notify_event(); } - return; } if (qemu_net_queue_flush(nc->incoming_queue)) { /* We emptied the queue successfully, signal to the IO thread to repoll @@ -689,6 +689,11 @@ static int net_init_nic(const NetClientOptions *opts, const char *name, error_report("invalid syntax for ethernet address"); return -1; } + if (nic->has_macaddr && + is_multicast_ether_addr(nd->macaddr.a)) { + error_report("NIC cannot have multicast MAC address (odd 1st byte)"); + return -1; + } qemu_macaddr_default_if_unset(&nd->macaddr); if (nic->has_vectors) { diff --git a/pc-bios/README b/pc-bios/README index be8dae0aa9..1501cf14b4 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-20130430. + built from git tag qemu-slof-20131015. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as diff --git a/pc-bios/keymaps/cz b/pc-bios/keymaps/cz new file mode 100644 index 0000000000..6584bfb189 --- /dev/null +++ b/pc-bios/keymaps/cz @@ -0,0 +1,94 @@ +include common + +# Czech qwertz layout +# comments are czech descriptions of the characters + +# ----------- +# First row +# ----------- + +# strednik, kolecko +semicolon 0x29 +dead_abovering 0x29 shift + +# numbers +plus 0x2 +1 0x2 shift +ecaron 0x3 +2 0x3 shift +scaron 0x4 +3 0x4 shift +ccaron 0x5 +4 0x5 shift +rcaron 0x6 +5 0x6 shift +zcaron 0x7 +6 0x7 shift +yacute 0x8 +7 0x8 shift +aacute 0x9 +8 0x9 shift +iacute 0xa +9 0xa shift +eacute 0xb +0 0xb shift + +# rovnitko +equal 0x0c +percent 0x0c shift + +# carka, hacek +dead_acute 0x0d +dead_caron 0x0d shift + +# ------------ +# Second row +# ------------ + +z 0x15 addupper + +# u s carkou, zpetne lomitko +uacute 0x1a +slash 0x1a shift + +# prava zavorka, leva zavorka +parenright 0x1b +parenleft 0x1b shift + +# ----------- +# Third row +# ----------- + +# u s krouzkem, uvozovky +uring 0x27 +quotedbl 0x27 shift + +# paragraf, vykricnik +section 0x28 +exclam 0x28 shift + +# vodorovna dvojtecka, apostrof +dead_diaeresis 0x2b +apostrophe 0x2b shift + +# ------------ +# Fourth row +# ------------ + +# zpetne lomitko, roura +backslash 0x2b +bar 0x2b shift + +y 0x2c addupper + +# carka, otaznik +comma 0x33 +question 0x33 shift + +# tecka, dvojtecka +period 0x34 +colon 0x34 shift + +# minus, podtrzitko +minus 0x35 +underscore 0x35 shift diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin index 092e58a46e..92a9831be7 100644 --- a/pc-bios/slof.bin +++ b/pc-bios/slof.bin Binary files differdiff --git a/qapi-schema.json b/qapi-schema.json index 60f3fd1db6..83fa4852ce 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -225,6 +225,27 @@ } } ## +# @ImageInfoSpecificVmdk: +# +# @create-type: The create type of VMDK image +# +# @cid: Content id of image +# +# @parent-cid: Parent VMDK image's cid +# +# @extents: List of extent files +# +# Since: 1.7 +## +{ 'type': 'ImageInfoSpecificVmdk', + 'data': { + 'create-type': 'str', + 'cid': 'int', + 'parent-cid': 'int', + 'extents': ['ImageInfo'] + } } + +## # @ImageInfoSpecific: # # A discriminated record of image format specific information structures. @@ -234,7 +255,8 @@ { 'union': 'ImageInfoSpecific', 'data': { - 'qcow2': 'ImageInfoSpecificQCow2' + 'qcow2': 'ImageInfoSpecificQCow2', + 'vmdk': 'ImageInfoSpecificVmdk' } } ## @@ -256,6 +278,8 @@ # # @encrypted: #optional true if the image is encrypted # +# @compressed: #optional true if the image is compressed (Since 1.7) +# # @backing-filename: #optional name of the backing file # # @full-backing-filename: #optional full path of the backing file @@ -276,7 +300,7 @@ { 'type': 'ImageInfo', 'data': {'filename': 'str', 'format': 'str', '*dirty-flag': 'bool', '*actual-size': 'int', 'virtual-size': 'int', - '*cluster-size': 'int', '*encrypted': 'bool', + '*cluster-size': 'int', '*encrypted': 'bool', '*compressed': 'bool', '*backing-filename': 'str', '*full-backing-filename': 'str', '*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'], '*backing-image': 'ImageInfo', @@ -1712,7 +1736,8 @@ # @existing: QEMU should look for an existing image file. # # @absolute-paths: QEMU should create a new image with absolute paths -# for the backing file. +# for the backing file. If there is no backing file available, the new +# image will not be backed either. # # Since: 1.1 ## @@ -1914,7 +1939,7 @@ # # Since: 0.14.0 # -# Notes: This command only exists as a stop-gap. It's use is highly +# Notes: This command only exists as a stop-gap. Its use is highly # discouraged. The semantics of this command are not guaranteed. # # Known limitations: diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index 1334de33cc..dc53545fa5 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -76,6 +76,24 @@ static void qapi_dealloc_end_struct(Visitor *v, Error **errp) } } +static void qapi_dealloc_start_implicit_struct(Visitor *v, + void **obj, + size_t size, + Error **errp) +{ + QapiDeallocVisitor *qov = to_qov(v); + qapi_dealloc_push(qov, obj); +} + +static void qapi_dealloc_end_implicit_struct(Visitor *v, Error **errp) +{ + QapiDeallocVisitor *qov = to_qov(v); + void **obj = qapi_dealloc_pop(qov); + if (obj) { + g_free(*obj); + } +} + static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp) { QapiDeallocVisitor *qov = to_qov(v); @@ -162,6 +180,8 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void) v->visitor.start_struct = qapi_dealloc_start_struct; v->visitor.end_struct = qapi_dealloc_end_struct; + v->visitor.start_implicit_struct = qapi_dealloc_start_implicit_struct; + v->visitor.end_implicit_struct = qapi_dealloc_end_implicit_struct; v->visitor.start_list = qapi_dealloc_start_list; v->visitor.next_list = qapi_dealloc_next_list; v->visitor.end_list = qapi_dealloc_end_list; diff --git a/qdev-monitor.c b/qdev-monitor.c index a02c925cb3..dc37a43dd9 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -453,11 +453,12 @@ static BusState *qbus_find(const char *path) DeviceState *qdev_device_add(QemuOpts *opts) { - ObjectClass *obj; - DeviceClass *k; + ObjectClass *oc; + DeviceClass *dc; const char *driver, *path, *id; - DeviceState *qdev; + DeviceState *dev; BusState *bus = NULL; + Error *err = NULL; driver = qemu_opt_get(opts, "driver"); if (!driver) { @@ -466,22 +467,28 @@ DeviceState *qdev_device_add(QemuOpts *opts) } /* find driver */ - obj = object_class_by_name(driver); - if (!obj) { + oc = object_class_by_name(driver); + if (!oc) { const char *typename = find_typename_by_alias(driver); if (typename) { driver = typename; - obj = object_class_by_name(driver); + oc = object_class_by_name(driver); } } - if (!obj) { + if (!oc) { qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", "device type"); return NULL; } - k = DEVICE_CLASS(obj); + if (object_class_is_abstract(oc)) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", + "non-abstract device type"); + return NULL; + } + + dc = DEVICE_CLASS(oc); /* find bus */ path = qemu_opt_get(opts, "bus"); @@ -490,16 +497,16 @@ DeviceState *qdev_device_add(QemuOpts *opts) if (!bus) { return NULL; } - if (!object_dynamic_cast(OBJECT(bus), k->bus_type)) { + if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) { qerror_report(QERR_BAD_BUS_FOR_DEVICE, driver, object_get_typename(OBJECT(bus))); return NULL; } - } else if (k->bus_type != NULL) { - bus = qbus_find_recursive(sysbus_get_default(), NULL, k->bus_type); + } else if (dc->bus_type != NULL) { + bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type); if (!bus) { qerror_report(QERR_NO_BUS_FOR_DEVICE, - k->bus_type, driver); + dc->bus_type, driver); return NULL; } } @@ -509,36 +516,42 @@ DeviceState *qdev_device_add(QemuOpts *opts) } /* create device, set properties */ - qdev = DEVICE(object_new(driver)); + dev = DEVICE(object_new(driver)); if (bus) { - qdev_set_parent_bus(qdev, bus); + qdev_set_parent_bus(dev, bus); } id = qemu_opts_id(opts); if (id) { - qdev->id = id; + dev->id = id; } - if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) { - qdev_free(qdev); + if (qemu_opt_foreach(opts, set_property, dev, 1) != 0) { + object_unparent(OBJECT(dev)); + object_unref(OBJECT(dev)); return NULL; } - if (qdev->id) { - object_property_add_child(qdev_get_peripheral(), qdev->id, - OBJECT(qdev), NULL); + if (dev->id) { + object_property_add_child(qdev_get_peripheral(), dev->id, + OBJECT(dev), NULL); } else { static int anon_count; gchar *name = g_strdup_printf("device[%d]", anon_count++); object_property_add_child(qdev_get_peripheral_anon(), name, - OBJECT(qdev), NULL); + OBJECT(dev), NULL); g_free(name); - } - if (qdev_init(qdev) < 0) { + } + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err != NULL) { + qerror_report_err(err); + error_free(err); + object_unparent(OBJECT(dev)); + object_unref(OBJECT(dev)); qerror_report(QERR_DEVICE_INIT_FAILED, driver); return NULL; } - qdev->opts = opts; - return qdev; + dev->opts = opts; + return dev; } diff --git a/qemu-coroutine-sleep.c b/qemu-coroutine-sleep.c index f6db978c1d..ad78fbaa2a 100644 --- a/qemu-coroutine-sleep.c +++ b/qemu-coroutine-sleep.c @@ -13,6 +13,7 @@ #include "block/coroutine.h" #include "qemu/timer.h" +#include "block/aio.h" typedef struct CoSleepCB { QEMUTimer *ts; @@ -37,3 +38,16 @@ void coroutine_fn co_sleep_ns(QEMUClockType type, int64_t ns) timer_del(sleep_cb.ts); timer_free(sleep_cb.ts); } + +void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type, + int64_t ns) +{ + CoSleepCB sleep_cb = { + .co = qemu_coroutine_self(), + }; + sleep_cb.ts = aio_timer_new(ctx, type, SCALE_NS, co_sleep_cb, &sleep_cb); + timer_mod(sleep_cb.ts, qemu_clock_get_ns(type) + ns); + qemu_coroutine_yield(); + timer_del(sleep_cb.ts); + timer_free(sleep_cb.ts); +} diff --git a/qemu-img.c b/qemu-img.c index 926f0a0feb..b6b5644cb6 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -607,7 +607,7 @@ static int img_check(int argc, char **argv) if (output_format == OFORMAT_HUMAN) { error_report("This image format does not support checks"); } - ret = 1; + ret = 63; goto fail; } @@ -1020,10 +1020,10 @@ static int img_compare(int argc, char **argv) } ret = compare_sectors(buf1, buf2, nb_sectors, &pnum); if (ret || pnum != nb_sectors) { - ret = 1; qprintf(quiet, "Content mismatch at offset %" PRId64 "!\n", sectors_to_bytes( ret ? sector_num : sector_num + pnum)); + ret = 1; goto out; } } @@ -1045,9 +1045,9 @@ static int img_compare(int argc, char **argv) } if (ret) { if (ret < 0) { - ret = 4; error_report("Error while reading offset %" PRId64 ": %s", sectors_to_bytes(sector_num), strerror(-ret)); + ret = 4; } goto out; } @@ -1092,10 +1092,10 @@ static int img_compare(int argc, char **argv) filename_over, buf1, quiet); if (ret) { if (ret < 0) { - ret = 4; error_report("Error while reading offset %" PRId64 " of %s: %s", sectors_to_bytes(sector_num), filename_over, strerror(-ret)); + ret = 4; } goto out; } diff --git a/qemu-options.hx b/qemu-options.hx index 5dc8b75cdb..8b9426484d 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1605,7 +1605,7 @@ to disable script execution. If running QEMU as an unprivileged user, use the network helper @var{helper} to configure the TAP interface. The default network -helper executable is @file{/usr/local/libexec/qemu-bridge-helper}. +helper executable is @file{/path/to/qemu-bridge-helper}. @option{fd}=@var{h} can be used to specify the handle of an already opened host TAP interface. @@ -1629,7 +1629,7 @@ qemu-system-i386 linux.img \ #launch a QEMU instance with the default network helper to #connect a TAP device to bridge br0 qemu-system-i386 linux.img \ - -net nic -net tap,"helper=/usr/local/libexec/qemu-bridge-helper" + -net nic -net tap,"helper=/path/to/qemu-bridge-helper" @end example @item -netdev bridge,id=@var{id}[,br=@var{bridge}][,helper=@var{helper}] @@ -1638,7 +1638,7 @@ Connect a host TAP network interface to a host bridge device. Use the network helper @var{helper} to configure the TAP interface and attach it to the bridge. The default network helper executable is -@file{/usr/local/libexec/qemu-bridge-helper} and the default bridge +@file{/path/to/qemu-bridge-helper} and the default bridge device is @file{br0}. Examples: diff --git a/qemu.nsi b/qemu.nsi index 0dc1f52693..cc5fafd579 100644 --- a/qemu.nsi +++ b/qemu.nsi @@ -60,7 +60,11 @@ InstallDir $PROGRAMFILES\qemu ; Registry key to check for directory (so if you install again, it will ; overwrite the old one automatically) -InstallDirRegKey HKLM "Software\qemu" "Install_Dir" +!ifdef W64 +InstallDirRegKey HKLM "Software\qemu64" "Install_Dir" +!else +InstallDirRegKey HKLM "Software\qemu32" "Install_Dir" +!endif ; Request administrator privileges for Windows Vista. RequestExecutionLevel admin diff --git a/qga/commands-posix.c b/qga/commands-posix.c index f453132b92..10682f58dc 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -99,7 +99,7 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) reopen_fd_to_null(1); reopen_fd_to_null(2); - execle("/sbin/shutdown", "shutdown", shutdown_flag, "+0", + execle("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0", "hypervisor initiated shutdown", (char*)NULL, environ); _exit(EXIT_FAILURE); } else if (pid < 0) { diff --git a/qga/vss-win32/requester.h b/qga/vss-win32/requester.h index cffec01791..374f9b8d16 100644 --- a/qga/vss-win32/requester.h +++ b/qga/vss-win32/requester.h @@ -13,6 +13,7 @@ #ifndef VSS_WIN32_REQUESTER_H #define VSS_WIN32_REQUESTER_H +#include <basetyps.h> /* STDAPI */ #include "qemu/compiler.h" #ifdef __cplusplus diff --git a/qom/object.c b/qom/object.c index e90e3827d9..fc19cf676a 100644 --- a/qom/object.c +++ b/qom/object.c @@ -838,8 +838,9 @@ char *object_property_get_str(Object *obj, const char *name, void object_property_set_link(Object *obj, Object *value, const char *name, Error **errp) { - object_property_set_str(obj, object_get_canonical_path(value), - name, errp); + gchar *path = object_get_canonical_path(value); + object_property_set_str(obj, path, name, errp); + g_free(path); } Object *object_property_get_link(Object *obj, const char *name, @@ -1344,6 +1345,66 @@ static char *qdev_get_type(Object *obj, Error **errp) return g_strdup(object_get_typename(obj)); } +static void property_get_uint8_ptr(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + uint8_t value = *(uint8_t *)opaque; + visit_type_uint8(v, &value, name, errp); +} + +static void property_get_uint16_ptr(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + uint16_t value = *(uint16_t *)opaque; + visit_type_uint16(v, &value, name, errp); +} + +static void property_get_uint32_ptr(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + uint32_t value = *(uint32_t *)opaque; + visit_type_uint32(v, &value, name, errp); +} + +static void property_get_uint64_ptr(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + uint64_t value = *(uint64_t *)opaque; + visit_type_uint64(v, &value, name, errp); +} + +void object_property_add_uint8_ptr(Object *obj, const char *name, + const uint8_t *v, Error **errp) +{ + object_property_add(obj, name, "uint8", property_get_uint8_ptr, + NULL, NULL, (void *)v, errp); +} + +void object_property_add_uint16_ptr(Object *obj, const char *name, + const uint16_t *v, Error **errp) +{ + object_property_add(obj, name, "uint16", property_get_uint16_ptr, + NULL, NULL, (void *)v, errp); +} + +void object_property_add_uint32_ptr(Object *obj, const char *name, + const uint32_t *v, Error **errp) +{ + object_property_add(obj, name, "uint32", property_get_uint32_ptr, + NULL, NULL, (void *)v, errp); +} + +void object_property_add_uint64_ptr(Object *obj, const char *name, + const uint64_t *v, Error **errp) +{ + object_property_add(obj, name, "uint64", property_get_uint64_ptr, + NULL, NULL, (void *)v, errp); +} + static void object_instance_init(Object *obj) { object_property_add_str(obj, "type", qdev_get_type, NULL, NULL); diff --git a/roms/SLOF b/roms/SLOF -Subproject 8cfdfc43f4c4c8c8dfa4b7cf16f7c19c84eee81 +Subproject e2e8ac901e617573ea383f9cffd136146d0675a diff --git a/savevm.c b/savevm.c index 2f631d4045..3f912ddcf9 100644 --- a/savevm.c +++ b/savevm.c @@ -794,7 +794,7 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) if (l > size) l = size; memcpy(f->buf + f->buf_index, buf, l); - f->bytes_xfer += size; + f->bytes_xfer += l; if (f->ops->writev_buffer) { add_to_iovec(f, f->buf + f->buf_index, l); } diff --git a/scripts/acpi_extract.py b/scripts/acpi_extract.py new file mode 100755 index 0000000000..22ea468102 --- /dev/null +++ b/scripts/acpi_extract.py @@ -0,0 +1,362 @@ +#!/usr/bin/python +# Copyright (C) 2011 Red Hat, Inc., Michael S. Tsirkin <mst@redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see <http://www.gnu.org/licenses/>. + +# Process mixed ASL/AML listing (.lst file) produced by iasl -l +# Locate and execute ACPI_EXTRACT directives, output offset info +# +# Documentation of ACPI_EXTRACT_* directive tags: +# +# These directive tags output offset information from AML for BIOS runtime +# table generation. +# Each directive is of the form: +# ACPI_EXTRACT_<TYPE> <array_name> <Operator> (...) +# and causes the extractor to create an array +# named <array_name> with offset, in the generated AML, +# of an object of a given type in the following <Operator>. +# +# A directive must fit on a single code line. +# +# Object type in AML is verified, a mismatch causes a build failure. +# +# Directives and operators currently supported are: +# ACPI_EXTRACT_NAME_DWORD_CONST - extract a Dword Const object from Name() +# ACPI_EXTRACT_NAME_WORD_CONST - extract a Word Const object from Name() +# ACPI_EXTRACT_NAME_BYTE_CONST - extract a Byte Const object from Name() +# ACPI_EXTRACT_METHOD_STRING - extract a NameString from Method() +# ACPI_EXTRACT_NAME_STRING - extract a NameString from Name() +# ACPI_EXTRACT_PROCESSOR_START - start of Processor() block +# ACPI_EXTRACT_PROCESSOR_STRING - extract a NameString from Processor() +# ACPI_EXTRACT_PROCESSOR_END - offset at last byte of Processor() + 1 +# ACPI_EXTRACT_PKG_START - start of Package block +# +# ACPI_EXTRACT_ALL_CODE - create an array storing the generated AML bytecode +# +# ACPI_EXTRACT is not allowed anywhere else in code, except in comments. + +import re; +import sys; +import fileinput; + +aml = [] +asl = [] +output = {} +debug = "" + +class asl_line: + line = None + lineno = None + aml_offset = None + +def die(diag): + sys.stderr.write("Error: %s; %s\n" % (diag, debug)) + sys.exit(1) + +#Store an ASL command, matching AML offset, and input line (for debugging) +def add_asl(lineno, line): + l = asl_line() + l.line = line + l.lineno = lineno + l.aml_offset = len(aml) + asl.append(l) + +#Store an AML byte sequence +#Verify that offset output by iasl matches # of bytes so far +def add_aml(offset, line): + o = int(offset, 16); + # Sanity check: offset must match size of code so far + if (o != len(aml)): + die("Offset 0x%x != 0x%x" % (o, len(aml))) + # Strip any trailing dots and ASCII dump after " + line = re.sub(r'\s*\.*\s*".*$',"", line) + # Strip traling whitespace + line = re.sub(r'\s+$',"", line) + # Strip leading whitespace + line = re.sub(r'^\s+',"", line) + # Split on whitespace + code = re.split(r'\s+', line) + for c in code: + # Require a legal hex number, two digits + if (not(re.search(r'^[0-9A-Fa-f][0-9A-Fa-f]$', c))): + die("Unexpected octet %s" % c); + aml.append(int(c, 16)); + +# Process aml bytecode array, decoding AML +def aml_pkglen_bytes(offset): + # PkgLength can be multibyte. Bits 8-7 give the # of extra bytes. + pkglenbytes = aml[offset] >> 6; + return pkglenbytes + 1 + +def aml_pkglen(offset): + pkgstart = offset + pkglenbytes = aml_pkglen_bytes(offset) + pkglen = aml[offset] & 0x3F + # If multibyte, first nibble only uses bits 0-3 + if ((pkglenbytes > 1) and (pkglen & 0x30)): + die("PkgLen bytes 0x%x but first nibble 0x%x expected 0x0X" % + (pkglen, pkglen)) + offset += 1 + pkglenbytes -= 1 + for i in range(pkglenbytes): + pkglen |= aml[offset + i] << (i * 8 + 4) + if (len(aml) < pkgstart + pkglen): + die("PckgLen 0x%x at offset 0x%x exceeds AML size 0x%x" % + (pkglen, offset, len(aml))) + return pkglen + +# Given method offset, find its NameString offset +def aml_method_string(offset): + #0x14 MethodOp PkgLength NameString MethodFlags TermList + if (aml[offset] != 0x14): + die( "Method offset 0x%x: expected 0x14 actual 0x%x" % + (offset, aml[offset])); + offset += 1; + pkglenbytes = aml_pkglen_bytes(offset) + offset += pkglenbytes; + return offset; + +# Given name offset, find its NameString offset +def aml_name_string(offset): + #0x08 NameOp NameString DataRef + if (aml[offset] != 0x08): + die( "Name offset 0x%x: expected 0x08 actual 0x%x" % + (offset, aml[offset])); + offset += 1 + # Block Name Modifier. Skip it. + if (aml[offset] == 0x5c or aml[offset] == 0x5e): + offset += 1 + return offset; + +# Given data offset, find 8 byte buffer offset +def aml_data_buffer8(offset): + #0x08 NameOp NameString DataRef + expect = [0x11, 0x0B, 0x0A, 0x08] + if (aml[offset:offset+4] != expect): + die( "Name offset 0x%x: expected %s actual %s" % + (offset, aml[offset:offset+4], expect)) + return offset + len(expect) + +# Given data offset, find dword const offset +def aml_data_dword_const(offset): + #0x08 NameOp NameString DataRef + if (aml[offset] != 0x0C): + die( "Name offset 0x%x: expected 0x0C actual 0x%x" % + (offset, aml[offset])); + return offset + 1; + +# Given data offset, find word const offset +def aml_data_word_const(offset): + #0x08 NameOp NameString DataRef + if (aml[offset] != 0x0B): + die( "Name offset 0x%x: expected 0x0B actual 0x%x" % + (offset, aml[offset])); + return offset + 1; + +# Given data offset, find byte const offset +def aml_data_byte_const(offset): + #0x08 NameOp NameString DataRef + if (aml[offset] != 0x0A): + die( "Name offset 0x%x: expected 0x0A actual 0x%x" % + (offset, aml[offset])); + return offset + 1; + +# Find name'd buffer8 +def aml_name_buffer8(offset): + return aml_data_buffer8(aml_name_string(offset) + 4) + +# Given name offset, find dword const offset +def aml_name_dword_const(offset): + return aml_data_dword_const(aml_name_string(offset) + 4) + +# Given name offset, find word const offset +def aml_name_word_const(offset): + return aml_data_word_const(aml_name_string(offset) + 4) + +# Given name offset, find byte const offset +def aml_name_byte_const(offset): + return aml_data_byte_const(aml_name_string(offset) + 4) + +def aml_device_start(offset): + #0x5B 0x82 DeviceOp PkgLength NameString + if ((aml[offset] != 0x5B) or (aml[offset + 1] != 0x82)): + die( "Name offset 0x%x: expected 0x5B 0x82 actual 0x%x 0x%x" % + (offset, aml[offset], aml[offset + 1])); + return offset + +def aml_device_string(offset): + #0x5B 0x82 DeviceOp PkgLength NameString + start = aml_device_start(offset) + offset += 2 + pkglenbytes = aml_pkglen_bytes(offset) + offset += pkglenbytes + return offset + +def aml_device_end(offset): + start = aml_device_start(offset) + offset += 2 + pkglenbytes = aml_pkglen_bytes(offset) + pkglen = aml_pkglen(offset) + return offset + pkglen + +def aml_processor_start(offset): + #0x5B 0x83 ProcessorOp PkgLength NameString ProcID + if ((aml[offset] != 0x5B) or (aml[offset + 1] != 0x83)): + die( "Name offset 0x%x: expected 0x5B 0x83 actual 0x%x 0x%x" % + (offset, aml[offset], aml[offset + 1])); + return offset + +def aml_processor_string(offset): + #0x5B 0x83 ProcessorOp PkgLength NameString ProcID + start = aml_processor_start(offset) + offset += 2 + pkglenbytes = aml_pkglen_bytes(offset) + offset += pkglenbytes + return offset + +def aml_processor_end(offset): + start = aml_processor_start(offset) + offset += 2 + pkglenbytes = aml_pkglen_bytes(offset) + pkglen = aml_pkglen(offset) + return offset + pkglen + +def aml_package_start(offset): + offset = aml_name_string(offset) + 4 + # 0x12 PkgLength NumElements PackageElementList + if (aml[offset] != 0x12): + die( "Name offset 0x%x: expected 0x12 actual 0x%x" % + (offset, aml[offset])); + offset += 1 + return offset + aml_pkglen_bytes(offset) + 1 + +lineno = 0 +for line in fileinput.input(): + # Strip trailing newline + line = line.rstrip(); + # line number and debug string to output in case of errors + lineno = lineno + 1 + debug = "input line %d: %s" % (lineno, line) + #ASL listing: space, then line#, then ...., then code + pasl = re.compile('^\s+([0-9]+)(:\s\s|\.\.\.\.)\s*') + m = pasl.search(line) + if (m): + add_asl(lineno, pasl.sub("", line)); + # AML listing: offset in hex, then ...., then code + paml = re.compile('^([0-9A-Fa-f]+)(:\s\s|\.\.\.\.)\s*') + m = paml.search(line) + if (m): + add_aml(m.group(1), paml.sub("", line)) + +# Now go over code +# Track AML offset of a previous non-empty ASL command +prev_aml_offset = -1 +for i in range(len(asl)): + debug = "input line %d: %s" % (asl[i].lineno, asl[i].line) + + l = asl[i].line + + # skip if not an extract directive + a = len(re.findall(r'ACPI_EXTRACT', l)) + if (not a): + # If not empty, store AML offset. Will be used for sanity checks + # IASL seems to put {}. at random places in the listing. + # Ignore any non-words for the purpose of this test. + m = re.search(r'\w+', l) + if (m): + prev_aml_offset = asl[i].aml_offset + continue + + if (a > 1): + die("Expected at most one ACPI_EXTRACT per line, actual %d" % a) + + mext = re.search(r''' + ^\s* # leading whitespace + /\*\s* # start C comment + (ACPI_EXTRACT_\w+) # directive: group(1) + \s+ # whitspace separates directive from array name + (\w+) # array name: group(2) + \s*\*/ # end of C comment + \s*$ # trailing whitespace + ''', l, re.VERBOSE) + if (not mext): + die("Stray ACPI_EXTRACT in input") + + # previous command must have produced some AML, + # otherwise we are in a middle of a block + if (prev_aml_offset == asl[i].aml_offset): + die("ACPI_EXTRACT directive in the middle of a block") + + directive = mext.group(1) + array = mext.group(2) + offset = asl[i].aml_offset + + if (directive == "ACPI_EXTRACT_ALL_CODE"): + if array in output: + die("%s directive used more than once" % directive) + output[array] = aml + continue + if (directive == "ACPI_EXTRACT_NAME_BUFFER8"): + offset = aml_name_buffer8(offset) + elif (directive == "ACPI_EXTRACT_NAME_DWORD_CONST"): + offset = aml_name_dword_const(offset) + elif (directive == "ACPI_EXTRACT_NAME_WORD_CONST"): + offset = aml_name_word_const(offset) + elif (directive == "ACPI_EXTRACT_NAME_BYTE_CONST"): + offset = aml_name_byte_const(offset) + elif (directive == "ACPI_EXTRACT_NAME_STRING"): + offset = aml_name_string(offset) + elif (directive == "ACPI_EXTRACT_METHOD_STRING"): + offset = aml_method_string(offset) + elif (directive == "ACPI_EXTRACT_DEVICE_START"): + offset = aml_device_start(offset) + elif (directive == "ACPI_EXTRACT_DEVICE_STRING"): + offset = aml_device_string(offset) + elif (directive == "ACPI_EXTRACT_DEVICE_END"): + offset = aml_device_end(offset) + elif (directive == "ACPI_EXTRACT_PROCESSOR_START"): + offset = aml_processor_start(offset) + elif (directive == "ACPI_EXTRACT_PROCESSOR_STRING"): + offset = aml_processor_string(offset) + elif (directive == "ACPI_EXTRACT_PROCESSOR_END"): + offset = aml_processor_end(offset) + elif (directive == "ACPI_EXTRACT_PKG_START"): + offset = aml_package_start(offset) + else: + die("Unsupported directive %s" % directive) + + if array not in output: + output[array] = [] + output[array].append(offset) + +debug = "at end of file" + +def get_value_type(maxvalue): + #Use type large enough to fit the table + if (maxvalue >= 0x10000): + return "int" + elif (maxvalue >= 0x100): + return "short" + else: + return "char" + +# Pretty print output +for array in output.keys(): + otype = get_value_type(max(output[array])) + odata = [] + for value in output[array]: + odata.append("0x%x" % value) + sys.stdout.write("static unsigned %s %s[] = {\n" % (otype, array)) + sys.stdout.write(",\n".join(odata)) + sys.stdout.write('\n};\n'); diff --git a/scripts/acpi_extract_preprocess.py b/scripts/acpi_extract_preprocess.py new file mode 100755 index 0000000000..69d10d621c --- /dev/null +++ b/scripts/acpi_extract_preprocess.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +# Copyright (C) 2011 Red Hat, Inc., Michael S. Tsirkin <mst@redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see <http://www.gnu.org/licenses/>. + +# Read a preprocessed ASL listing and put each ACPI_EXTRACT +# directive in a comment, to make iasl skip it. +# We also put each directive on a new line, the machinery +# in tools/acpi_extract.py requires this. + +import re; +import sys; +import fileinput; + +def die(diag): + sys.stderr.write("Error: %s\n" % (diag)) + sys.exit(1) + +# Note: () around pattern make split return matched string as part of list +psplit = re.compile(r''' ( + \b # At word boundary + ACPI_EXTRACT_\w+ # directive + \s+ # some whitespace + \w+ # array name + )''', re.VERBOSE); + +lineno = 0 +for line in fileinput.input(): + # line number and debug string to output in case of errors + lineno = lineno + 1 + debug = "input line %d: %s" % (lineno, line.rstrip()) + + s = psplit.split(line); + # The way split works, each odd item is the matching ACPI_EXTRACT directive. + # Put each in a comment, and on a line by itself. + for i in range(len(s)): + if (i % 2): + sys.stdout.write("\n/* %s */\n" % s[i]) + else: + sys.stdout.write(s[i]) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index c39e6284b8..65f1a54ee7 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -20,7 +20,10 @@ import errno def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None): substructs = [] ret = '' - full_name = name if not fn_prefix else "%s_%s" % (name, fn_prefix) + if not fn_prefix: + full_name = name + else: + full_name = "%s_%s" % (name, fn_prefix) for argname, argentry, optional, structured in parse_args(members): if structured: @@ -97,7 +100,10 @@ if (!error_is_set(errp)) { ''') push_indent() - full_name = name if not field_prefix else "%s_%s" % (field_prefix, name) + if not field_prefix: + full_name = name + else: + full_name = "%s_%s" % (field_prefix, name) if len(field_prefix): ret += mcgen(''' @@ -283,12 +289,17 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** name=name) pop_indent() + + if not discriminator: + desc_type = "type" + else: + desc_type = discriminator ret += mcgen(''' visit_type_%(name)sKind(m, &(*obj)->kind, "%(type)s", &err); if (!err) { switch ((*obj)->kind) { ''', - name=name, type="type" if not discriminator else discriminator) + name=name, type=desc_type) for key in members: if not discriminator: diff --git a/scripts/update-acpi.sh b/scripts/update-acpi.sh new file mode 100644 index 0000000000..b5f05ff3cf --- /dev/null +++ b/scripts/update-acpi.sh @@ -0,0 +1,4 @@ +cd x86_64-softmmu +for file in hw/i386/*.hex; do + cp -f $file ../$file.generated +done diff --git a/slirp/if.c b/slirp/if.c index 87ca8a53a9..fb7acf87dd 100644 --- a/slirp/if.c +++ b/slirp/if.c @@ -142,7 +142,7 @@ diddit: /* * Send a packet - * We choose a packet based on it's position in the output queues; + * We choose a packet based on its position in the output queues; * If there are packets on the fastq, they are sent FIFO, before * everything else. Otherwise we choose the first packet from the * batchq and send it. the next packet chosen will be from the session diff --git a/target-alpha/translate.c b/target-alpha/translate.c index c24910f6a1..1155e86e29 100644 --- a/target-alpha/translate.c +++ b/target-alpha/translate.c @@ -1601,7 +1601,7 @@ static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) tcg_temp_free(pc); /* Since the destination is running in PALmode, we don't really - need the page permissions check. We'll see the existance of + need the page permissions check. We'll see the existence of the page when we create the TB, and we'll flush all TBs if we change the PAL base register. */ if (!ctx->singlestep_enabled && !(ctx->tb->cflags & CF_LAST_IO)) { diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 2c56740bf6..9f110f15b6 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -176,6 +176,7 @@ typedef struct CPUARMState { uint32_t c9_pmxevtyper; /* perf monitor event type */ uint32_t c9_pmuserenr; /* perf monitor user enable */ uint32_t c9_pminten; /* perf monitor interrupt enables */ + uint32_t c12_vbar; /* vector base address register */ uint32_t c13_fcse; /* FCSE PID. */ uint32_t c13_context; /* Context ID. */ uint32_t c13_tls1; /* User RW Thread register. */ diff --git a/target-arm/helper.c b/target-arm/helper.c index c63bbd7fc1..3445813465 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -225,10 +225,16 @@ static void count_cpreg(gpointer key, gpointer opaque) static gint cpreg_key_compare(gconstpointer a, gconstpointer b) { - uint32_t aidx = *(uint32_t *)a; - uint32_t bidx = *(uint32_t *)b; + uint64_t aidx = cpreg_to_kvm_id(*(uint32_t *)a); + uint64_t bidx = cpreg_to_kvm_id(*(uint32_t *)b); - return aidx - bidx; + if (aidx > bidx) { + return 1; + } + if (aidx < bidx) { + return -1; + } + return 0; } static void cpreg_make_keylist(gpointer key, gpointer value, gpointer udata) @@ -537,6 +543,13 @@ static int pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, return 0; } +static int vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->cp15.c12_vbar = value & ~0x1Ful; + return 0; +} + static int ccsidr_read(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t *value) { @@ -622,6 +635,10 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_NO_MIGRATE, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .resetvalue = 0, .writefn = pmintenclr_write, }, + { .name = "VBAR", .cp = 15, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .writefn = vbar_write, + .fieldoffset = offsetof(CPUARMState, cp15.c12_vbar), + .resetvalue = 0 }, { .name = "SCR", .cp = 15, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c1_scr), .resetvalue = 0, }, @@ -2470,7 +2487,17 @@ void arm_cpu_do_interrupt(CPUState *cs) } /* High vectors. */ if (env->cp15.c1_sys & (1 << 13)) { + /* when enabled, base address cannot be remapped. */ addr += 0xffff0000; + } else { + /* ARM v7 architectures provide a vector base address register to remap + * the interrupt vector table. + * This register is only followed in non-monitor mode, and has a secure + * and un-secure copy. Since the cpu is always in a un-secure operation + * and is never in monitor mode this feature is always active. + * Note: only bits 31:5 are valid. + */ + addr += env->cp15.c12_vbar; } switch_mode (env, new_mode); env->spsr = cpsr_read(env); diff --git a/target-arm/kvm.c b/target-arm/kvm.c index b92e00dae0..6e5cd36fae 100644 --- a/target-arm/kvm.c +++ b/target-arm/kvm.c @@ -67,7 +67,13 @@ static bool reg_syncs_via_tuple_list(uint64_t regidx) static int compare_u64(const void *a, const void *b) { - return *(uint64_t *)a - *(uint64_t *)b; + if (*(uint64_t *)a > *(uint64_t *)b) { + return 1; + } + if (*(uint64_t *)a < *(uint64_t *)b) { + return -1; + } + return 0; } int kvm_arch_init_vcpu(CPUState *cs) diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h index 7508cf5a06..e1415f043c 100644 --- a/target-microblaze/cpu.h +++ b/target-microblaze/cpu.h @@ -246,6 +246,7 @@ struct CPUMBState { /* lwx/swx reserved address */ #define RES_ADDR_NONE 0xffffffff /* Use 0xffffffff to indicate no reservation */ uint32_t res_addr; + uint32_t res_val; /* Internal flags. */ #define IMM_FLAG 4 diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c index 1b937b3f0d..9edcb67e66 100644 --- a/target-microblaze/translate.c +++ b/target-microblaze/translate.c @@ -49,6 +49,8 @@ static TCGv env_imm; static TCGv env_btaken; static TCGv env_btarget; static TCGv env_iflags; +static TCGv env_res_addr; +static TCGv env_res_val; #include "exec/gen-icount.h" @@ -150,6 +152,10 @@ static void read_carry(DisasContext *dc, TCGv d) tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31); } +/* + * write_carry sets the carry bits in MSR based on bit 0 of v. + * v[31:1] are ignored. + */ static void write_carry(DisasContext *dc, TCGv v) { TCGv t0 = tcg_temp_new(); @@ -162,10 +168,10 @@ static void write_carry(DisasContext *dc, TCGv v) tcg_temp_free(t0); } -static void write_carryi(DisasContext *dc, int carry) +static void write_carryi(DisasContext *dc, bool carry) { TCGv t0 = tcg_temp_new(); - tcg_gen_movi_tl(t0, carry ? 1 : 0); + tcg_gen_movi_tl(t0, carry); write_carry(dc, t0); tcg_temp_free(t0); } @@ -386,10 +392,7 @@ static void dec_and(DisasContext *dc) return; if (not) { - TCGv t = tcg_temp_new(); - tcg_gen_not_tl(t, *(dec_alu_op_b(dc))); - tcg_gen_and_tl(cpu_R[dc->rd], cpu_R[dc->ra], t); - tcg_temp_free(t); + tcg_gen_andc_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } else tcg_gen_and_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } @@ -749,7 +752,7 @@ static void dec_barrel(DisasContext *dc) static void dec_bit(DisasContext *dc) { - TCGv t0, t1; + TCGv t0; unsigned int op; int mem_index = cpu_mmu_index(dc->env); @@ -760,32 +763,22 @@ static void dec_bit(DisasContext *dc) t0 = tcg_temp_new(); LOG_DIS("src r%d r%d\n", dc->rd, dc->ra); - tcg_gen_andi_tl(t0, cpu_R[dc->ra], 1); + tcg_gen_andi_tl(t0, cpu_SR[SR_MSR], MSR_CC); + write_carry(dc, cpu_R[dc->ra]); if (dc->rd) { - t1 = tcg_temp_new(); - read_carry(dc, t1); - tcg_gen_shli_tl(t1, t1, 31); - tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); - tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->rd], t1); - tcg_temp_free(t1); + tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->rd], t0); } - - /* Update carry. */ - write_carry(dc, t0); tcg_temp_free(t0); break; case 0x1: case 0x41: /* srl. */ - t0 = tcg_temp_new(); LOG_DIS("srl r%d r%d\n", dc->rd, dc->ra); - /* Update carry. */ - tcg_gen_andi_tl(t0, cpu_R[dc->ra], 1); - write_carry(dc, t0); - tcg_temp_free(t0); + /* Update carry. Note that write carry only looks at the LSB. */ + write_carry(dc, cpu_R[dc->ra]); if (dc->rd) { if (op == 0x41) tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); @@ -872,7 +865,7 @@ static void dec_imm(DisasContext *dc) } static inline void gen_load(DisasContext *dc, TCGv dst, TCGv addr, - unsigned int size) + unsigned int size, bool exclusive) { int mem_index = cpu_mmu_index(dc->env); @@ -884,6 +877,11 @@ static inline void gen_load(DisasContext *dc, TCGv dst, TCGv addr, tcg_gen_qemu_ld32u(dst, addr, mem_index); } else cpu_abort(dc->env, "Incorrect load size %d\n", size); + + if (exclusive) { + tcg_gen_mov_tl(env_res_addr, addr); + tcg_gen_mov_tl(env_res_val, dst); + } } static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) @@ -1055,7 +1053,7 @@ static void dec_load(DisasContext *dc) * into v. If the load succeeds, we verify alignment of the * address and if that succeeds we write into the destination reg. */ - gen_load(dc, v, *addr, size); + gen_load(dc, v, *addr, size, ex); tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd), @@ -1070,20 +1068,19 @@ static void dec_load(DisasContext *dc) tcg_temp_free(v); } else { if (dc->rd) { - gen_load(dc, cpu_R[dc->rd], *addr, size); + gen_load(dc, cpu_R[dc->rd], *addr, size, ex); if (rev) { dec_byteswap(dc, cpu_R[dc->rd], cpu_R[dc->rd], size); } } else { /* We are loading into r0, no need to reverse. */ - gen_load(dc, env_imm, *addr, size); + gen_load(dc, env_imm, *addr, size, ex); } } if (ex) { /* lwx */ /* no support for for AXI exclusive so always clear C */ write_carryi(dc, 0); - tcg_gen_st_tl(*addr, cpu_env, offsetof(CPUMBState, res_addr)); } if (addr == &t) @@ -1107,7 +1104,7 @@ static void gen_store(DisasContext *dc, TCGv addr, TCGv val, static void dec_store(DisasContext *dc) { - TCGv t, *addr, swx_addr, r_check; + TCGv t, *addr, swx_addr; int swx_skip = 0; unsigned int size, rev = 0, ex = 0; @@ -1131,9 +1128,9 @@ static void dec_store(DisasContext *dc) sync_jmpstate(dc); addr = compute_ldst_addr(dc, &t); - r_check = tcg_temp_new(); swx_addr = tcg_temp_local_new(); if (ex) { /* swx */ + TCGv tval; /* Force addr into the swx_addr. */ tcg_gen_mov_tl(swx_addr, *addr); @@ -1141,11 +1138,20 @@ static void dec_store(DisasContext *dc) /* swx does not throw unaligned access errors, so force alignment */ tcg_gen_andi_tl(swx_addr, swx_addr, ~3); - tcg_gen_ld_tl(r_check, cpu_env, offsetof(CPUMBState, res_addr)); write_carryi(dc, 1); swx_skip = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, r_check, swx_addr, swx_skip); + tcg_gen_brcond_tl(TCG_COND_NE, env_res_addr, swx_addr, swx_skip); + + /* Compare the value loaded at lwx with current contents of + the reserved location. + FIXME: This only works for system emulation where we can expect + this compare and the following write to be atomic. For user + emulation we need to add atomicity between threads. */ + tval = tcg_temp_new(); + gen_load(dc, tval, swx_addr, 4, false); + tcg_gen_brcond_tl(TCG_COND_NE, env_res_val, tval, swx_skip); write_carryi(dc, 0); + tcg_temp_free(tval); } if (rev && size != 4) { @@ -1227,7 +1233,6 @@ static void dec_store(DisasContext *dc) if (ex) { gen_set_label(swx_skip); } - tcg_temp_free(r_check); tcg_temp_free(swx_addr); if (addr == &t) @@ -2014,6 +2019,12 @@ void mb_tcg_init(void) env_btaken = tcg_global_mem_new(TCG_AREG0, offsetof(CPUMBState, btaken), "btaken"); + env_res_addr = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUMBState, res_addr), + "res_addr"); + env_res_val = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUMBState, res_val), + "res_val"); for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { cpu_R[i] = tcg_global_mem_new(TCG_AREG0, offsetof(CPUMBState, regs[i]), diff --git a/target-openrisc/cpu.h b/target-openrisc/cpu.h index 8fd0bc0bf0..0f9efdf6de 100644 --- a/target-openrisc/cpu.h +++ b/target-openrisc/cpu.h @@ -373,6 +373,7 @@ void cpu_openrisc_pic_init(OpenRISCCPU *cpu); /* hw/openrisc_timer.c */ void cpu_openrisc_clock_init(OpenRISCCPU *cpu); void cpu_openrisc_count_update(OpenRISCCPU *cpu); +void cpu_openrisc_timer_update(OpenRISCCPU *cpu); void cpu_openrisc_count_start(OpenRISCCPU *cpu); void cpu_openrisc_count_stop(OpenRISCCPU *cpu); diff --git a/target-openrisc/interrupt.c b/target-openrisc/interrupt.c index 16ef4b3e79..2153e7ea7e 100644 --- a/target-openrisc/interrupt.c +++ b/target-openrisc/interrupt.c @@ -30,26 +30,15 @@ void openrisc_cpu_do_interrupt(CPUState *cs) OpenRISCCPU *cpu = OPENRISC_CPU(cs); CPUOpenRISCState *env = &cpu->env; #ifndef CONFIG_USER_ONLY - if (env->flags & D_FLAG) { /* Delay Slot insn */ + + env->epcr = env->pc; + if (env->flags & D_FLAG) { env->flags &= ~D_FLAG; env->sr |= SR_DSX; - if (env->exception_index == EXCP_TICK || - env->exception_index == EXCP_INT || - env->exception_index == EXCP_SYSCALL || - env->exception_index == EXCP_FPE) { - env->epcr = env->jmp_pc; - } else { - env->epcr = env->pc - 4; - } - } else { - if (env->exception_index == EXCP_TICK || - env->exception_index == EXCP_INT || - env->exception_index == EXCP_SYSCALL || - env->exception_index == EXCP_FPE) { - env->epcr = env->npc; - } else { - env->epcr = env->pc; - } + env->epcr -= 4; + } + if (env->exception_index == EXCP_SYSCALL) { + env->epcr += 4; } /* For machine-state changed between user-mode and supervisor mode, diff --git a/target-openrisc/mmu.c b/target-openrisc/mmu.c index 22d7cbec18..dd487bd0d1 100644 --- a/target-openrisc/mmu.c +++ b/target-openrisc/mmu.c @@ -32,7 +32,7 @@ int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu, int *prot, target_ulong address, int rw) { *physical = address; - *prot = PAGE_READ | PAGE_WRITE; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TLBRET_MATCH; } @@ -187,7 +187,7 @@ int cpu_openrisc_handle_mmu_fault(CPUOpenRISCState *env, if (ret == TLBRET_MATCH) { tlb_set_page(env, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot | PAGE_EXEC, + physical & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); ret = 0; } else if (ret < 0) { diff --git a/target-openrisc/sys_helper.c b/target-openrisc/sys_helper.c index cccbc0e939..be06c4565b 100644 --- a/target-openrisc/sys_helper.c +++ b/target-openrisc/sys_helper.c @@ -81,7 +81,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, case TO_SPR(0, 64): /* ESR */ env->esr = rb; break; - case TO_SPR(1, 512) ... TO_SPR(1, 639): /* DTLBW0MR 0-127 */ + case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); if (!(rb & 1)) { tlb_flush_page(env, env->tlb->dtlb[0][idx].mr & TARGET_PAGE_MASK); @@ -89,7 +89,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, env->tlb->dtlb[0][idx].mr = rb; break; - case TO_SPR(1, 640) ... TO_SPR(1, 767): /* DTLBW0TR 0-127 */ + case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); env->tlb->dtlb[0][idx].tr = rb; break; @@ -100,7 +100,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, case TO_SPR(1, 1280) ... TO_SPR(1, 1407): /* DTLBW3MR 0-127 */ case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */ break; - case TO_SPR(2, 512) ... TO_SPR(2, 639): /* ITLBW0MR 0-127 */ + case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); if (!(rb & 1)) { tlb_flush_page(env, env->tlb->itlb[0][idx].mr & TARGET_PAGE_MASK); @@ -108,7 +108,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, env->tlb->itlb[0][idx].mr = rb; break; - case TO_SPR(2, 640) ... TO_SPR(2, 767): /* ITLBW0TR 0-127 */ + case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); env->tlb->itlb[0][idx].tr = rb; break; @@ -127,33 +127,31 @@ void HELPER(mtspr)(CPUOpenRISCState *env, break; case TO_SPR(10, 0): /* TTMR */ { + if ((env->ttmr & TTMR_M) ^ (rb & TTMR_M)) { + switch (rb & TTMR_M) { + case TIMER_NONE: + cpu_openrisc_count_stop(cpu); + break; + case TIMER_INTR: + case TIMER_SHOT: + case TIMER_CONT: + cpu_openrisc_count_start(cpu); + break; + default: + break; + } + } + int ip = env->ttmr & TTMR_IP; if (rb & TTMR_IP) { /* Keep IP bit. */ - env->ttmr = (rb & ~TTMR_IP) + ip; + env->ttmr = (rb & ~TTMR_IP) | ip; } else { /* Clear IP bit. */ env->ttmr = rb & ~TTMR_IP; cs->interrupt_request &= ~CPU_INTERRUPT_TIMER; } - cpu_openrisc_count_update(cpu); - - switch (env->ttmr & TTMR_M) { - case TIMER_NONE: - cpu_openrisc_count_stop(cpu); - break; - case TIMER_INTR: - cpu_openrisc_count_start(cpu); - break; - case TIMER_SHOT: - cpu_openrisc_count_start(cpu); - break; - case TIMER_CONT: - cpu_openrisc_count_start(cpu); - break; - default: - break; - } + cpu_openrisc_timer_update(cpu); } break; @@ -162,7 +160,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, if (env->ttmr & TIMER_NONE) { return; } - cpu_openrisc_count_start(cpu); + cpu_openrisc_timer_update(cpu); break; default: @@ -214,11 +212,11 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, case TO_SPR(0, 64): /* ESR */ return env->esr; - case TO_SPR(1, 512) ... TO_SPR(1, 639): /* DTLBW0MR 0-127 */ + case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); return env->tlb->dtlb[0][idx].mr; - case TO_SPR(1, 640) ... TO_SPR(1, 767): /* DTLBW0TR 0-127 */ + case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); return env->tlb->dtlb[0][idx].tr; @@ -230,11 +228,11 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */ break; - case TO_SPR(2, 512) ... TO_SPR(2, 639): /* ITLBW0MR 0-127 */ + case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); return env->tlb->itlb[0][idx].mr; - case TO_SPR(2, 640) ... TO_SPR(2, 767): /* ITLBW0TR 0-127 */ + case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); return env->tlb->itlb[0][idx].tr; diff --git a/target-openrisc/translate.c b/target-openrisc/translate.c index 8908a2e32b..91c60ebaae 100644 --- a/target-openrisc/translate.c +++ b/target-openrisc/translate.c @@ -209,42 +209,49 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) static void gen_jump(DisasContext *dc, uint32_t imm, uint32_t reg, uint32_t op0) { target_ulong tmp_pc; - int lab = gen_new_label(); - TCGv sr_f = tcg_temp_new(); /* N26, 26bits imm */ tmp_pc = sign_extend((imm<<2), 26) + dc->pc; - tcg_gen_andi_tl(sr_f, cpu_sr, SR_F); - if (op0 == 0x00) { /* l.j */ + switch (op0) { + case 0x00: /* l.j */ tcg_gen_movi_tl(jmp_pc, tmp_pc); - } else if (op0 == 0x01) { /* l.jal */ + break; + case 0x01: /* l.jal */ tcg_gen_movi_tl(cpu_R[9], (dc->pc + 8)); tcg_gen_movi_tl(jmp_pc, tmp_pc); - } else if (op0 == 0x03) { /* l.bnf */ - tcg_gen_movi_tl(jmp_pc, dc->pc+8); - tcg_gen_brcondi_i32(TCG_COND_EQ, sr_f, SR_F, lab); - tcg_gen_movi_tl(jmp_pc, tmp_pc); - gen_set_label(lab); - } else if (op0 == 0x04) { /* l.bf */ - tcg_gen_movi_tl(jmp_pc, dc->pc+8); - tcg_gen_brcondi_i32(TCG_COND_NE, sr_f, SR_F, lab); - tcg_gen_movi_tl(jmp_pc, tmp_pc); - gen_set_label(lab); - } else if (op0 == 0x11) { /* l.jr */ + break; + case 0x03: /* l.bnf */ + case 0x04: /* l.bf */ + { + int lab = gen_new_label(); + TCGv sr_f = tcg_temp_new(); + tcg_gen_movi_tl(jmp_pc, dc->pc+8); + tcg_gen_andi_tl(sr_f, cpu_sr, SR_F); + tcg_gen_brcondi_i32(op0 == 0x03 ? TCG_COND_EQ : TCG_COND_NE, + sr_f, SR_F, lab); + tcg_gen_movi_tl(jmp_pc, tmp_pc); + gen_set_label(lab); + tcg_temp_free(sr_f); + } + break; + case 0x11: /* l.jr */ tcg_gen_mov_tl(jmp_pc, cpu_R[reg]); - } else if (op0 == 0x12) { /* l.jalr */ + break; + case 0x12: /* l.jalr */ tcg_gen_movi_tl(cpu_R[9], (dc->pc + 8)); tcg_gen_mov_tl(jmp_pc, cpu_R[reg]); - } else { + break; + default: gen_illegal_exception(dc); + break; } - tcg_temp_free(sr_f); dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; gen_sync_flags(dc); } + static void dec_calc(DisasContext *dc, uint32_t insn) { uint32_t op0, op1, op2; @@ -904,29 +911,33 @@ static void dec_misc(DisasContext *dc, uint32_t insn) case 0x27: /* l.addi */ LOG_DIS("l.addi r%d, r%d, %d\n", rd, ra, I16); { - int lab = gen_new_label(); - TCGv_i64 ta = tcg_temp_new_i64(); - TCGv_i64 td = tcg_temp_local_new_i64(); - TCGv_i32 res = tcg_temp_local_new_i32(); - TCGv_i32 sr_ove = tcg_temp_local_new_i32(); - tcg_gen_extu_i32_i64(ta, cpu_R[ra]); - tcg_gen_addi_i64(td, ta, sign_extend(I16, 16)); - tcg_gen_trunc_i64_i32(res, td); - tcg_gen_shri_i64(td, td, 32); - tcg_gen_andi_i64(td, td, 0x3); - /* Jump to lab when no overflow. */ - tcg_gen_brcondi_i64(TCG_COND_EQ, td, 0x0, lab); - tcg_gen_brcondi_i64(TCG_COND_EQ, td, 0x3, lab); - tcg_gen_ori_i32(cpu_sr, cpu_sr, (SR_OV | SR_CY)); - tcg_gen_andi_i32(sr_ove, cpu_sr, SR_OVE); - tcg_gen_brcondi_i32(TCG_COND_NE, sr_ove, SR_OVE, lab); - gen_exception(dc, EXCP_RANGE); - gen_set_label(lab); - tcg_gen_mov_i32(cpu_R[rd], res); - tcg_temp_free_i64(ta); - tcg_temp_free_i64(td); - tcg_temp_free_i32(res); - tcg_temp_free_i32(sr_ove); + if (I16 == 0) { + tcg_gen_mov_tl(cpu_R[rd], cpu_R[ra]); + } else { + int lab = gen_new_label(); + TCGv_i64 ta = tcg_temp_new_i64(); + TCGv_i64 td = tcg_temp_local_new_i64(); + TCGv_i32 res = tcg_temp_local_new_i32(); + TCGv_i32 sr_ove = tcg_temp_local_new_i32(); + tcg_gen_extu_i32_i64(ta, cpu_R[ra]); + tcg_gen_addi_i64(td, ta, sign_extend(I16, 16)); + tcg_gen_trunc_i64_i32(res, td); + tcg_gen_shri_i64(td, td, 32); + tcg_gen_andi_i64(td, td, 0x3); + /* Jump to lab when no overflow. */ + tcg_gen_brcondi_i64(TCG_COND_EQ, td, 0x0, lab); + tcg_gen_brcondi_i64(TCG_COND_EQ, td, 0x3, lab); + tcg_gen_ori_i32(cpu_sr, cpu_sr, (SR_OV | SR_CY)); + tcg_gen_andi_i32(sr_ove, cpu_sr, SR_OVE); + tcg_gen_brcondi_i32(TCG_COND_NE, sr_ove, SR_OVE, lab); + gen_exception(dc, EXCP_RANGE); + gen_set_label(lab); + tcg_gen_mov_i32(cpu_R[rd], res); + tcg_temp_free_i64(ta); + tcg_temp_free_i64(td); + tcg_temp_free_i32(res); + tcg_temp_free_i32(sr_ove); + } } break; diff --git a/target-ppc/Makefile.objs b/target-ppc/Makefile.objs index 94d6d0c43b..3cb23e0f11 100644 --- a/target-ppc/Makefile.objs +++ b/target-ppc/Makefile.objs @@ -2,7 +2,7 @@ obj-y += cpu-models.o obj-y += translate.o ifeq ($(CONFIG_SOFTMMU),y) obj-y += machine.o mmu_helper.o mmu-hash32.o -obj-$(TARGET_PPC64) += mmu-hash64.o +obj-$(TARGET_PPC64) += mmu-hash64.o arch_dump.o endif obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o diff --git a/target-ppc/arch_dump.c b/target-ppc/arch_dump.c new file mode 100644 index 0000000000..17fd4c6fb1 --- /dev/null +++ b/target-ppc/arch_dump.c @@ -0,0 +1,253 @@ +/* + * writing ELF notes for ppc64 arch + * + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "cpu.h" +#include "elf.h" +#include "exec/cpu-all.h" +#include "sysemu/dump.h" +#include "sysemu/kvm.h" + +struct PPC64UserRegStruct { + uint64_t gpr[32]; + uint64_t nip; + uint64_t msr; + uint64_t orig_gpr3; + uint64_t ctr; + uint64_t link; + uint64_t xer; + uint64_t ccr; + uint64_t softe; + uint64_t trap; + uint64_t dar; + uint64_t dsisr; + uint64_t result; +} QEMU_PACKED; + +struct PPC64ElfPrstatus { + char pad1[112]; + struct PPC64UserRegStruct pr_reg; + uint64_t pad2[4]; +} QEMU_PACKED; + + +struct PPC64ElfFpregset { + uint64_t fpr[32]; + uint64_t fpscr; +} QEMU_PACKED; + + +struct PPC64ElfVmxregset { + ppc_avr_t avr[32]; + ppc_avr_t vscr; + union { + ppc_avr_t unused; + uint32_t value; + } vrsave; +} QEMU_PACKED; + +struct PPC64ElfVsxregset { + uint64_t vsr[32]; +} QEMU_PACKED; + +struct PPC64ElfSperegset { + uint32_t evr[32]; + uint64_t spe_acc; + uint32_t spe_fscr; +} QEMU_PACKED; + +typedef struct noteStruct { + Elf64_Nhdr hdr; + char name[5]; + char pad3[3]; + union { + struct PPC64ElfPrstatus prstatus; + struct PPC64ElfFpregset fpregset; + struct PPC64ElfVmxregset vmxregset; + struct PPC64ElfVsxregset vsxregset; + struct PPC64ElfSperegset speregset; + } contents; +} QEMU_PACKED Note; + + +static void ppc64_write_elf64_prstatus(Note *note, PowerPCCPU *cpu) +{ + int i; + uint64_t cr; + struct PPC64ElfPrstatus *prstatus; + struct PPC64UserRegStruct *reg; + + note->hdr.n_type = cpu_to_be32(NT_PRSTATUS); + + prstatus = ¬e->contents.prstatus; + memset(prstatus, 0, sizeof(*prstatus)); + reg = &prstatus->pr_reg; + + for (i = 0; i < 32; i++) { + reg->gpr[i] = cpu_to_be64(cpu->env.gpr[i]); + } + reg->nip = cpu_to_be64(cpu->env.nip); + reg->msr = cpu_to_be64(cpu->env.msr); + reg->ctr = cpu_to_be64(cpu->env.ctr); + reg->link = cpu_to_be64(cpu->env.lr); + reg->xer = cpu_to_be64(cpu_read_xer(&cpu->env)); + + cr = 0; + for (i = 0; i < 8; i++) { + cr |= (cpu->env.crf[i] & 15) << (4 * (7 - i)); + } + reg->ccr = cpu_to_be64(cr); +} + +static void ppc64_write_elf64_fpregset(Note *note, PowerPCCPU *cpu) +{ + int i; + struct PPC64ElfFpregset *fpregset; + + note->hdr.n_type = cpu_to_be32(NT_PRFPREG); + + fpregset = ¬e->contents.fpregset; + memset(fpregset, 0, sizeof(*fpregset)); + + for (i = 0; i < 32; i++) { + fpregset->fpr[i] = cpu_to_be64(cpu->env.fpr[i]); + } + fpregset->fpscr = cpu_to_be64(cpu->env.fpscr); +} + +static void ppc64_write_elf64_vmxregset(Note *note, PowerPCCPU *cpu) +{ + int i; + struct PPC64ElfVmxregset *vmxregset; + + note->hdr.n_type = cpu_to_be32(NT_PPC_VMX); + vmxregset = ¬e->contents.vmxregset; + memset(vmxregset, 0, sizeof(*vmxregset)); + + for (i = 0; i < 32; i++) { + vmxregset->avr[i].u64[0] = cpu_to_be64(cpu->env.avr[i].u64[0]); + vmxregset->avr[i].u64[1] = cpu_to_be64(cpu->env.avr[i].u64[1]); + } + vmxregset->vscr.u32[3] = cpu_to_be32(cpu->env.vscr); +} +static void ppc64_write_elf64_vsxregset(Note *note, PowerPCCPU *cpu) +{ + int i; + struct PPC64ElfVsxregset *vsxregset; + + note->hdr.n_type = cpu_to_be32(NT_PPC_VSX); + vsxregset = ¬e->contents.vsxregset; + memset(vsxregset, 0, sizeof(*vsxregset)); + + for (i = 0; i < 32; i++) { + vsxregset->vsr[i] = cpu_to_be64(cpu->env.vsr[i]); + } +} +static void ppc64_write_elf64_speregset(Note *note, PowerPCCPU *cpu) +{ + struct PPC64ElfSperegset *speregset; + note->hdr.n_type = cpu_to_be32(NT_PPC_SPE); + speregset = ¬e->contents.speregset; + memset(speregset, 0, sizeof(*speregset)); + + speregset->spe_acc = cpu_to_be64(cpu->env.spe_acc); + speregset->spe_fscr = cpu_to_be32(cpu->env.spe_fscr); +} + +struct NoteFuncDescStruct { + int contents_size; + void (*note_contents_func)(Note *note, PowerPCCPU *cpu); +} note_func[] = { + {sizeof(((Note *)0)->contents.prstatus), ppc64_write_elf64_prstatus}, + {sizeof(((Note *)0)->contents.fpregset), ppc64_write_elf64_fpregset}, + {sizeof(((Note *)0)->contents.vmxregset), ppc64_write_elf64_vmxregset}, + {sizeof(((Note *)0)->contents.vsxregset), ppc64_write_elf64_vsxregset}, + {sizeof(((Note *)0)->contents.speregset), ppc64_write_elf64_speregset}, + { 0, NULL} +}; + +typedef struct NoteFuncDescStruct NoteFuncDesc; + +int cpu_get_dump_info(ArchDumpInfo *info, + const struct GuestPhysBlockList *guest_phys_blocks) +{ + /* + * Currently only handling PPC64 big endian. + */ + info->d_machine = EM_PPC64; + info->d_endian = ELFDATA2MSB; + info->d_class = ELFCLASS64; + + return 0; +} + +ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) +{ + int name_size = 8; /* "CORE" or "QEMU" rounded */ + size_t elf_note_size = 0; + int note_head_size; + NoteFuncDesc *nf; + + if (class != ELFCLASS64) { + return -1; + } + assert(machine == EM_PPC64); + + note_head_size = sizeof(Elf64_Nhdr); + + for (nf = note_func; nf->note_contents_func; nf++) { + elf_note_size = elf_note_size + note_head_size + name_size + + nf->contents_size; + } + + return (elf_note_size) * nr_cpus; +} + +static int ppc64_write_all_elf64_notes(const char *note_name, + WriteCoreDumpFunction f, + PowerPCCPU *cpu, int id, + void *opaque) +{ + Note note; + int ret = -1; + int note_size; + NoteFuncDesc *nf; + + for (nf = note_func; nf->note_contents_func; nf++) { + note.hdr.n_namesz = cpu_to_be32(sizeof(note.name)); + note.hdr.n_descsz = cpu_to_be32(nf->contents_size); + strncpy(note.name, note_name, sizeof(note.name)); + + (*nf->note_contents_func)(¬e, cpu); + + note_size = sizeof(note) - sizeof(note.contents) + nf->contents_size; + ret = f(¬e, note_size, opaque); + if (ret < 0) { + return -1; + } + } + return 0; +} + +int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + return ppc64_write_all_elf64_notes("CORE", f, cpu, cpuid, opaque); +} + +int ppc64_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, + CPUState *cpu, void *opaque) +{ + return 0; +} diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index f3c710a9e5..827e5dd0e1 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -108,7 +108,10 @@ void ppc_cpu_dump_statistics(CPUState *cpu, FILE *f, hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int ppc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); - +int ppc64_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, + CPUState *cpu, void *opaque); +int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque); #ifndef CONFIG_USER_ONLY extern const struct VMStateDescription vmstate_ppc_cpu; #endif diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 422a6bbd2e..26acdba847 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -405,6 +405,7 @@ struct ppc_slb_t { uint64_t vsid; }; +#define MAX_SLB_ENTRIES 64 #define SEGMENT_SHIFT_256M 28 #define SEGMENT_MASK_256M (~((1ULL << SEGMENT_SHIFT_256M) - 1)) @@ -949,7 +950,7 @@ struct CPUPPCState { #if !defined(CONFIG_USER_ONLY) #if defined(TARGET_PPC64) /* PowerPC 64 SLB area */ - ppc_slb_t slb[64]; + ppc_slb_t slb[MAX_SLB_ENTRIES]; int32_t slb_nr; #endif /* segment registers */ diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 8a196c6cc1..b77ce5e94c 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -818,7 +818,7 @@ int kvm_arch_put_registers(CPUState *cs, int level) /* Sync SLB */ #ifdef TARGET_PPC64 - for (i = 0; i < 64; i++) { + for (i = 0; i < ARRAY_SIZE(env->slb); i++) { sregs.u.s.ppc64.slb[i].slbe = env->slb[i].esid; sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid; } @@ -1033,9 +1033,22 @@ int kvm_arch_get_registers(CPUState *cs) /* Sync SLB */ #ifdef TARGET_PPC64 - for (i = 0; i < 64; i++) { - ppc_store_slb(env, sregs.u.s.ppc64.slb[i].slbe, - sregs.u.s.ppc64.slb[i].slbv); + /* + * The packed SLB array we get from KVM_GET_SREGS only contains + * information about valid entries. So we flush our internal + * copy to get rid of stale ones, then put all valid SLB entries + * back in. + */ + memset(env->slb, 0, sizeof(env->slb)); + for (i = 0; i < ARRAY_SIZE(env->slb); i++) { + target_ulong rb = sregs.u.s.ppc64.slb[i].slbe; + target_ulong rs = sregs.u.s.ppc64.slb[i].slbv; + /* + * Only restore valid entries + */ + if (rb & SLB_ESID_V) { + ppc_store_slb(env, rb, rs); + } } #endif @@ -1789,6 +1802,20 @@ static int kvm_ppc_register_host_cpu_type(void) return 0; } +int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function) +{ + struct kvm_rtas_token_args args = { + .token = token, + }; + + if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_RTAS)) { + return -ENOENT; + } + + strncpy(args.name, function, sizeof(args.name)); + + return kvm_vm_ioctl(kvm_state, KVM_PPC_RTAS_DEFINE_TOKEN, &args); +} int kvmppc_get_htab_fd(bool write) { diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 4ae7bf2c32..5f78e4be14 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -38,6 +38,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); #endif /* !CONFIG_USER_ONLY */ int kvmppc_fixup_cpu(PowerPCCPU *cpu); bool kvmppc_has_cap_epr(void); +int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function); int kvmppc_get_htab_fd(bool write); int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns); int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, @@ -164,6 +165,12 @@ static inline bool kvmppc_has_cap_epr(void) return false; } +static inline int kvmppc_define_rtas_kernel_token(uint32_t token, + const char *function) +{ + return -1; +} + static inline int kvmppc_get_htab_fd(bool write) { return -1; diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 12e1512996..12c174f7f3 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -312,7 +312,7 @@ static const VMStateDescription vmstate_slb = { .minimum_version_id_old = 1, .fields = (VMStateField []) { VMSTATE_INT32_EQUAL(env.slb_nr, PowerPCCPU), - VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, 64), + VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), VMSTATE_END_OF_LIST() } }; diff --git a/target-ppc/mem_helper.c b/target-ppc/mem_helper.c index d8e63ca7d2..f35ed037c7 100644 --- a/target-ppc/mem_helper.c +++ b/target-ppc/mem_helper.c @@ -212,6 +212,7 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, int index = (addr & 0xf) >> sh; \ \ if (msr_le) { \ + index = n_elems - index - 1; \ r->element[LO_IDX ? index : (adjust - index)] = \ swap(access(env, addr)); \ } else { \ @@ -236,6 +237,7 @@ LVE(lvewx, cpu_ldl_data, bswap32, u32) int index = (addr & 0xf) >> sh; \ \ if (msr_le) { \ + index = n_elems - index - 1; \ access(env, addr, swap(r->element[LO_IDX ? index : \ (adjust - index)])); \ } else { \ diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 651da6b0d5..47825ac543 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -108,6 +108,11 @@ static void spr_write_clear (void *opaque, int sprn, int gprn) tcg_temp_free(t0); tcg_temp_free(t1); } + +static void spr_access_nop(void *opaque, int sprn, int gprn) +{ +} + #endif /* SPR common to all PowerPC */ @@ -1382,7 +1387,7 @@ static void gen_spr_74xx (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Not strictly an SPR */ vscr_init(env, 0x00010000); @@ -5170,7 +5175,7 @@ static void init_proc_750 (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ gen_tbl(env); @@ -5233,7 +5238,7 @@ static void init_proc_750cl (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ gen_tbl(env); @@ -5419,7 +5424,7 @@ static void init_proc_750cx (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ gen_tbl(env); @@ -5486,7 +5491,7 @@ static void init_proc_750fx (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ gen_tbl(env); @@ -5558,7 +5563,7 @@ static void init_proc_750gx (CPUPPCState *env) /* XXX : not implemented (XXX: different from 750fx) */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ gen_tbl(env); @@ -5694,7 +5699,7 @@ static void init_proc_755 (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* XXX : not implemented */ spr_register(env, SPR_L2PMCR, "L2PMCR", @@ -6650,7 +6655,7 @@ static void init_proc_970 (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Memory management */ /* XXX: not correct */ @@ -6750,7 +6755,7 @@ static void init_proc_970FX (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Memory management */ /* XXX: not correct */ @@ -6862,7 +6867,7 @@ static void init_proc_970GX (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Memory management */ /* XXX: not correct */ @@ -6962,7 +6967,7 @@ static void init_proc_970MP (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Memory management */ /* XXX: not correct */ @@ -7054,7 +7059,7 @@ static void init_proc_power5plus(CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Memory management */ /* XXX: not correct */ @@ -7103,6 +7108,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + dc->fw_name = "PowerPC,POWER5"; dc->desc = "POWER5+"; pcc->init_proc = init_proc_power5plus; pcc->check_pow = check_pow_970FX; @@ -7213,6 +7219,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + dc->fw_name = "PowerPC,POWER7"; dc->desc = "POWER7"; pcc->init_proc = init_proc_POWER7; pcc->check_pow = check_pow_nocheck; @@ -7247,6 +7254,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + dc->fw_name = "PowerPC,POWER8"; dc->desc = "POWER8"; pcc->init_proc = init_proc_POWER7; pcc->check_pow = check_pow_nocheck; @@ -8567,6 +8575,10 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = ppc_cpu_get_phys_page_debug; cc->vmsd = &vmstate_ppc_cpu; +#if defined(TARGET_PPC64) + cc->write_elf64_note = ppc64_cpu_write_elf64_note; + cc->write_elf64_qemunote = ppc64_cpu_write_elf64_qemunote; +#endif #endif cc->gdb_num_core_regs = 71; @@ -8575,6 +8587,8 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) #else cc->gdb_core_xml_file = "power-core.xml"; #endif + + dc->fw_name = "PowerPC,UNKNOWN"; } static const TypeInfo ppc_cpu_type_info = { diff --git a/target-xtensa/core-dc233c.c b/target-xtensa/core-dc233c.c index 11acbf3580..738d543e53 100644 --- a/target-xtensa/core-dc233c.c +++ b/target-xtensa/core-dc233c.c @@ -49,6 +49,7 @@ static const XtensaConfig dc233c = { EXCEPTIONS_SECTION, INTERRUPTS_SECTION, TLB_SECTION, + DEBUG_SECTION, .clock_freq_khz = 10000, }; diff --git a/tcg/ia64/tcg-target.c b/tcg/ia64/tcg-target.c index 0656d3907a..2d8e00cd94 100644 --- a/tcg/ia64/tcg-target.c +++ b/tcg/ia64/tcg-target.c @@ -109,7 +109,6 @@ enum { }; static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R33, TCG_REG_R35, TCG_REG_R36, TCG_REG_R37, @@ -226,6 +225,7 @@ enum { OPC_BR_CALL_SPTK_MANY_B5 = 0x02100001000ull, OPC_BR_RET_SPTK_MANY_B4 = 0x00108001100ull, OPC_BRL_SPTK_MANY_X3 = 0x18000001000ull, + OPC_BRL_CALL_SPTK_MANY_X4 = 0x1a000001000ull, OPC_CMP_LT_A6 = 0x18000000000ull, OPC_CMP_LTU_A6 = 0x1a000000000ull, OPC_CMP_EQ_A6 = 0x1c000000000ull, @@ -263,6 +263,7 @@ enum { OPC_MOV_I_I26 = 0x00150000000ull, OPC_MOVL_X2 = 0x0c000000000ull, OPC_OR_A1 = 0x10070000000ull, + OPC_OR_A3 = 0x10170000000ull, OPC_SETF_EXP_M18 = 0x0c748000000ull, OPC_SETF_SIG_M18 = 0x0c708000000ull, OPC_SHL_I7 = 0x0f240000000ull, @@ -281,9 +282,13 @@ enum { OPC_UNPACK4_L_I2 = 0x0f860000000ull, OPC_XMA_L_F2 = 0x1d000000000ull, OPC_XOR_A1 = 0x10078000000ull, + OPC_XOR_A3 = 0x10178000000ull, OPC_ZXT1_I29 = 0x00080000000ull, OPC_ZXT2_I29 = 0x00088000000ull, OPC_ZXT4_I29 = 0x00090000000ull, + + INSN_NOP_M = OPC_NOP_M48, /* nop.m 0 */ + INSN_NOP_I = OPC_NOP_I18, /* nop.i 0 */ }; static inline uint64_t tcg_opc_a1(int qp, uint64_t opc, int r1, @@ -581,6 +586,8 @@ static inline uint64_t tcg_opc_l3(uint64_t imm) return (imm & 0x07fffffffff00000ull) >> 18; } +#define tcg_opc_l4 tcg_opc_l3 + static inline uint64_t tcg_opc_m1(int qp, uint64_t opc, int r1, int r3) { return opc @@ -665,6 +672,15 @@ static inline uint64_t tcg_opc_x3(int qp, uint64_t opc, uint64_t imm) | (qp & 0x3f); } +static inline uint64_t tcg_opc_x4(int qp, uint64_t opc, int b1, uint64_t imm) +{ + return opc + | ((imm & 0x0800000000000000ull) >> 23) /* i */ + | ((imm & 0x00000000000fffffull) << 13) /* imm20b */ + | ((b1 & 0x7) << 6) + | (qp & 0x3f); +} + /* * Relocations @@ -851,20 +867,31 @@ static inline void tcg_out_bundle(TCGContext *s, int template, s->code_ptr += 16; } +static inline uint64_t tcg_opc_mov_a(int qp, TCGReg dst, TCGReg src) +{ + return tcg_opc_a4(qp, OPC_ADDS_A4, dst, 0, src); +} + static inline void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { tcg_out_bundle(s, mmI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, ret, 0, arg)); + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_mov_a(TCG_REG_P0, ret, arg)); +} + +static inline uint64_t tcg_opc_movi_a(int qp, TCGReg dst, int64_t src) +{ + assert(src == sextract64(src, 0, 22)); + return tcg_opc_a5(qp, OPC_ADDL_A5, dst, src, TCG_REG_R0); } static inline void tcg_out_movi(TCGContext *s, TCGType type, TCGReg reg, tcg_target_long arg) { tcg_out_bundle(s, mLX, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_l2 (arg), tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, reg, arg)); } @@ -877,8 +904,8 @@ static void tcg_out_br(TCGContext *s, int label_index) the existing value and using it again. This ensure that caches and memory are kept coherent during retranslation. */ tcg_out_bundle(s, mmB, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, + INSN_NOP_M, tcg_opc_b1 (TCG_REG_P0, OPC_BR_SPTK_MANY_B1, get_reloc_pcrel21b(s->code_ptr + 2))); @@ -890,7 +917,23 @@ static void tcg_out_br(TCGContext *s, int label_index) } } -static inline void tcg_out_call(TCGContext *s, TCGArg addr) +static inline void tcg_out_calli(TCGContext *s, uintptr_t addr) +{ + /* Look through the function descriptor. */ + uintptr_t disp, *desc = (uintptr_t *)addr; + tcg_out_bundle(s, mlx, + INSN_NOP_M, + tcg_opc_l2 (desc[1]), + tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, TCG_REG_R1, desc[1])); + disp = (desc[0] - (uintptr_t)s->code_ptr) >> 4; + tcg_out_bundle(s, mLX, + INSN_NOP_M, + tcg_opc_l4 (disp), + tcg_opc_x4 (TCG_REG_P0, OPC_BRL_CALL_SPTK_MANY_X4, + TCG_REG_B0, disp)); +} + +static inline void tcg_out_callr(TCGContext *s, TCGReg addr) { tcg_out_bundle(s, MmI, tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R2, addr), @@ -899,7 +942,7 @@ static inline void tcg_out_call(TCGContext *s, TCGArg addr) TCG_REG_B6, TCG_REG_R2, 0)); tcg_out_bundle(s, mmB, tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R3), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_b5 (TCG_REG_P0, OPC_BR_CALL_SPTK_MANY_B5, TCG_REG_B0, TCG_REG_B6)); } @@ -915,7 +958,7 @@ static void tcg_out_exit_tb(TCGContext *s, tcg_target_long arg) imm = (uint64_t)disp >> 4; tcg_out_bundle(s, mLX, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_l3 (imm), tcg_opc_x3 (TCG_REG_P0, OPC_BRL_SPTK_MANY_X3, imm)); } @@ -932,12 +975,12 @@ static inline void tcg_out_goto_tb(TCGContext *s, TCGArg arg) tcg_out_bundle(s, MmI, tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R2, TCG_REG_R2), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, TCG_REG_R2, 0)); tcg_out_bundle(s, mmB, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, + INSN_NOP_M, tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); } @@ -947,12 +990,12 @@ static inline void tcg_out_goto_tb(TCGContext *s, TCGArg arg) static inline void tcg_out_jmp(TCGContext *s, TCGArg addr) { tcg_out_bundle(s, mmI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, + INSN_NOP_M, tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, addr, 0)); tcg_out_bundle(s, mmB, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, + INSN_NOP_M, tcg_opc_b4(TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); } @@ -964,14 +1007,14 @@ static inline void tcg_out_ld_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg, tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R2, arg2, arg1), tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); } else { tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2); tcg_out_bundle(s, MmI, tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, TCG_REG_R2, arg1), tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); } } @@ -983,14 +1026,14 @@ static inline void tcg_out_st_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg, tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R2, arg2, arg1), tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); } else { tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2); tcg_out_bundle(s, MmI, tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, TCG_REG_R2, arg1), tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); } } @@ -1014,32 +1057,59 @@ static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, } } -static inline void tcg_out_alu(TCGContext *s, uint64_t opc_a1, TCGArg ret, - TCGArg arg1, int const_arg1, +static inline void tcg_out_alu(TCGContext *s, uint64_t opc_a1, uint64_t opc_a3, + TCGReg ret, TCGArg arg1, int const_arg1, TCGArg arg2, int const_arg2) { - uint64_t opc1, opc2; + uint64_t opc1 = 0, opc2 = 0, opc3 = 0; + if (const_arg2 && arg2 != 0) { + opc2 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R3, arg2); + arg2 = TCG_REG_R3; + } if (const_arg1 && arg1 != 0) { - opc1 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, - TCG_REG_R2, arg1, TCG_REG_R0); - arg1 = TCG_REG_R2; - } else { - opc1 = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); + if (opc_a3 && arg1 == (int8_t)arg1) { + opc3 = tcg_opc_a3(TCG_REG_P0, opc_a3, ret, arg1, arg2); + } else { + opc1 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R2, arg1); + arg1 = TCG_REG_R2; + } + } + if (opc3 == 0) { + opc3 = tcg_opc_a1(TCG_REG_P0, opc_a1, ret, arg1, arg2); } - if (const_arg2 && arg2 != 0) { - opc2 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, - TCG_REG_R3, arg2, TCG_REG_R0); - arg2 = TCG_REG_R3; + tcg_out_bundle(s, (opc1 || opc2 ? mII : miI), + opc1 ? opc1 : INSN_NOP_M, + opc2 ? opc2 : INSN_NOP_I, + opc3); +} + +static inline void tcg_out_add(TCGContext *s, TCGReg ret, TCGReg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2 && arg2 == sextract64(arg2, 0, 14)) { + tcg_out_bundle(s, mmI, + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, ret, arg2, arg1)); } else { - opc2 = tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0); + tcg_out_alu(s, OPC_ADD_A1, 0, ret, arg1, 0, arg2, const_arg2); } +} - tcg_out_bundle(s, mII, - opc1, - opc2, - tcg_opc_a1(TCG_REG_P0, opc_a1, ret, arg1, arg2)); +static inline void tcg_out_sub(TCGContext *s, TCGReg ret, TCGArg arg1, + int const_arg1, TCGArg arg2, int const_arg2) +{ + if (!const_arg1 && const_arg2 && -arg2 == sextract64(-arg2, 0, 14)) { + tcg_out_bundle(s, mmI, + INSN_NOP_M, + INSN_NOP_M, + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, ret, -arg2, arg1)); + } else { + tcg_out_alu(s, OPC_SUB_A1, OPC_SUB_A3, ret, + arg1, const_arg1, arg2, const_arg2); + } } static inline void tcg_out_eqv(TCGContext *s, TCGArg ret, @@ -1047,7 +1117,7 @@ static inline void tcg_out_eqv(TCGContext *s, TCGArg ret, TCGArg arg2, int const_arg2) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_a1 (TCG_REG_P0, OPC_XOR_A1, ret, arg1, arg2), tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); } @@ -1057,7 +1127,7 @@ static inline void tcg_out_nand(TCGContext *s, TCGArg ret, TCGArg arg2, int const_arg2) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_a1 (TCG_REG_P0, OPC_AND_A1, ret, arg1, arg2), tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); } @@ -1067,7 +1137,7 @@ static inline void tcg_out_nor(TCGContext *s, TCGArg ret, TCGArg arg2, int const_arg2) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, arg2), tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); } @@ -1077,7 +1147,7 @@ static inline void tcg_out_orc(TCGContext *s, TCGArg ret, TCGArg arg2, int const_arg2) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, TCG_REG_R2, -1, arg2), tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, TCG_REG_R2)); } @@ -1088,16 +1158,16 @@ static inline void tcg_out_mul(TCGContext *s, TCGArg ret, tcg_out_bundle(s, mmI, tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F6, arg1), tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F7, arg2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); tcg_out_bundle(s, mmF, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, + INSN_NOP_M, tcg_opc_f2 (TCG_REG_P0, OPC_XMA_L_F2, TCG_REG_F6, TCG_REG_F6, TCG_REG_F7, TCG_REG_F0)); tcg_out_bundle(s, miI, tcg_opc_m19(TCG_REG_P0, OPC_GETF_SIG_M19, ret, TCG_REG_F6), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I, + INSN_NOP_I); } static inline void tcg_out_sar_i32(TCGContext *s, TCGArg ret, TCGArg arg1, @@ -1105,8 +1175,8 @@ static inline void tcg_out_sar_i32(TCGContext *s, TCGArg ret, TCGArg arg1, { if (const_arg2) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11, ret, arg1, arg2, 31 - arg2)); } else { @@ -1124,14 +1194,14 @@ static inline void tcg_out_sar_i64(TCGContext *s, TCGArg ret, TCGArg arg1, { if (const_arg2) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11, ret, arg1, arg2, 63 - arg2)); } else { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret, arg1, arg2)); } } @@ -1141,13 +1211,13 @@ static inline void tcg_out_shl_i32(TCGContext *s, TCGArg ret, TCGArg arg1, { if (const_arg2) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg1, 63 - arg2, 31 - arg2)); } else { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R2, 0x1f, arg2), tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret, @@ -1160,14 +1230,14 @@ static inline void tcg_out_shl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, { if (const_arg2) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg1, 63 - arg2, 63 - arg2)); } else { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret, arg1, arg2)); } @@ -1178,8 +1248,8 @@ static inline void tcg_out_shr_i32(TCGContext *s, TCGArg ret, TCGArg arg1, { if (const_arg2) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, arg1, arg2, 31 - arg2)); } else { @@ -1197,14 +1267,14 @@ static inline void tcg_out_shr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, { if (const_arg2) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, arg1, arg2, 63 - arg2)); } else { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, arg1, arg2)); } @@ -1215,20 +1285,20 @@ static inline void tcg_out_rotl_i32(TCGContext *s, TCGArg ret, TCGArg arg1, { if (const_arg2) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, TCG_REG_R2, arg1, arg1), tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, TCG_REG_R2, 32 - arg2, 31)); } else { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, TCG_REG_R2, arg1, arg1), tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, 0x1f, arg2)); tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R3, 0x20, TCG_REG_R3), tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, @@ -1241,8 +1311,8 @@ static inline void tcg_out_rotl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, { if (const_arg2) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1, arg1, 0x40 - arg2)); } else { @@ -1254,8 +1324,8 @@ static inline void tcg_out_rotl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R2, arg1, TCG_REG_R2)); tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, TCG_REG_R2, TCG_REG_R3)); } @@ -1266,7 +1336,7 @@ static inline void tcg_out_rotr_i32(TCGContext *s, TCGArg ret, TCGArg arg1, { if (const_arg2) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, TCG_REG_R2, arg1, arg1), tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, @@ -1287,8 +1357,8 @@ static inline void tcg_out_rotr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, { if (const_arg2) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1, arg1, arg2)); } else { @@ -1300,44 +1370,63 @@ static inline void tcg_out_rotr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R2, arg1, TCG_REG_R2)); tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, TCG_REG_R2, TCG_REG_R3)); } } +static const uint64_t opc_ext_i29[8] = { + OPC_ZXT1_I29, OPC_ZXT2_I29, OPC_ZXT4_I29, 0, + OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0 +}; + +static inline uint64_t tcg_opc_ext_i(int qp, TCGMemOp opc, TCGReg d, TCGReg s) +{ + if ((opc & MO_SIZE) == MO_64) { + return tcg_opc_mov_a(qp, d, s); + } else { + return tcg_opc_i29(qp, opc_ext_i29[opc & MO_SSIZE], d, s); + } +} + static inline void tcg_out_ext(TCGContext *s, uint64_t opc_i29, TCGArg ret, TCGArg arg) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + INSN_NOP_I, tcg_opc_i29(TCG_REG_P0, opc_i29, ret, arg)); } +static inline uint64_t tcg_opc_bswap64_i(int qp, TCGReg d, TCGReg s) +{ + return tcg_opc_i3(qp, OPC_MUX1_I3, d, s, 0xb); +} + static inline void tcg_out_bswap16(TCGContext *s, TCGArg ret, TCGArg arg) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 15, 15), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, ret, 0xb)); + tcg_opc_bswap64_i(TCG_REG_P0, ret, ret)); } static inline void tcg_out_bswap32(TCGContext *s, TCGArg ret, TCGArg arg) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 31, 31), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, ret, 0xb)); + tcg_opc_bswap64_i(TCG_REG_P0, ret, ret)); } static inline void tcg_out_bswap64(TCGContext *s, TCGArg ret, TCGArg arg) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, arg, 0xb)); + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, ret, arg)); } static inline void tcg_out_deposit(TCGContext *s, TCGArg ret, TCGArg a1, @@ -1357,8 +1446,7 @@ static inline void tcg_out_deposit(TCGContext *s, TCGArg ret, TCGArg a1, } else { /* Otherwise, load any constant into a temporary. Do this into the first I slot to help out with cross-unit delays. */ - i1 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, - TCG_REG_R2, a2, TCG_REG_R0); + i1 = tcg_opc_movi_a(TCG_REG_P0, TCG_REG_R2, a2); a2 = TCG_REG_R2; } } @@ -1366,8 +1454,8 @@ static inline void tcg_out_deposit(TCGContext *s, TCGArg ret, TCGArg a1, i2 = tcg_opc_i15(TCG_REG_P0, OPC_DEP_I15, ret, a2, a1, cpos, lm1); } tcg_out_bundle(s, (i1 ? mII : miI), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - i1 ? i1 : tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_M, + i1 ? i1 : INSN_NOP_I, i2); } @@ -1413,38 +1501,16 @@ static inline uint64_t tcg_opc_cmp_a(int qp, TCGCond cond, TCGArg arg1, } } -static inline void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGArg arg1, - int const_arg1, TCGArg arg2, int const_arg2, - int label_index, int cmp4) +static inline void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, + TCGReg arg2, int label_index, int cmp4) { TCGLabel *l = &s->labels[label_index]; - uint64_t opc1, opc2; - - if (const_arg1 && arg1 != 0) { - opc1 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R2, - arg1, TCG_REG_R0); - arg1 = TCG_REG_R2; - } else { - opc1 = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); - } - - if (const_arg2 && arg2 != 0) { - opc2 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R3, - arg2, TCG_REG_R0); - arg2 = TCG_REG_R3; - } else { - opc2 = tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0); - } - tcg_out_bundle(s, mII, - opc1, - opc2, - tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4)); - tcg_out_bundle(s, mmB, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_b1 (TCG_REG_P6, OPC_BR_DPTK_FEW_B1, - get_reloc_pcrel21b(s->code_ptr + 2))); + tcg_out_bundle(s, miB, + INSN_NOP_M, + tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4), + tcg_opc_b1(TCG_REG_P6, OPC_BR_DPTK_FEW_B1, + get_reloc_pcrel21b(s->code_ptr + 2))); if (l->has_value) { reloc_pcrel21b((s->code_ptr - 16) + 2, l->u.value); @@ -1459,8 +1525,8 @@ static inline void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGArg ret, { tcg_out_bundle(s, MmI, tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4), - tcg_opc_a5(TCG_REG_P6, OPC_ADDL_A5, ret, 1, TCG_REG_R0), - tcg_opc_a5(TCG_REG_P7, OPC_ADDL_A5, ret, 0, TCG_REG_R0)); + tcg_opc_movi_a(TCG_REG_P6, ret, 1), + tcg_opc_movi_a(TCG_REG_P7, ret, 0)); } static inline void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGArg ret, @@ -1471,18 +1537,18 @@ static inline void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGArg ret, uint64_t opc1, opc2; if (const_v1) { - opc1 = tcg_opc_a5(TCG_REG_P6, OPC_ADDL_A5, ret, v1, TCG_REG_R0); + opc1 = tcg_opc_movi_a(TCG_REG_P6, ret, v1); } else if (ret == v1) { - opc1 = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); + opc1 = INSN_NOP_M; } else { - opc1 = tcg_opc_a4(TCG_REG_P6, OPC_ADDS_A4, ret, 0, v1); + opc1 = tcg_opc_mov_a(TCG_REG_P6, ret, v1); } if (const_v2) { - opc2 = tcg_opc_a5(TCG_REG_P7, OPC_ADDL_A5, ret, v2, TCG_REG_R0); + opc2 = tcg_opc_movi_a(TCG_REG_P7, ret, v2); } else if (ret == v2) { - opc2 = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); + opc2 = INSN_NOP_I; } else { - opc2 = tcg_opc_a4(TCG_REG_P7, OPC_ADDS_A4, ret, 0, v2); + opc2 = tcg_opc_mov_a(TCG_REG_P7, ret, v2); } tcg_out_bundle(s, MmI, @@ -1496,11 +1562,11 @@ static inline void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGArg ret, R2 is loaded with the address of the addend TLB entry. R57 is loaded with the address, zero extented on 32-bit targets. */ static inline void tcg_out_qemu_tlb(TCGContext *s, TCGArg addr_reg, - int s_bits, uint64_t offset_rw, + TCGMemOp s_bits, uint64_t offset_rw, uint64_t offset_addend) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, TCG_REG_R2, addr_reg, TARGET_PAGE_BITS, CPU_TLB_BITS - 1), tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, TCG_REG_R2, @@ -1509,12 +1575,9 @@ static inline void tcg_out_qemu_tlb(TCGContext *s, TCGArg addr_reg, tcg_out_bundle(s, mII, tcg_opc_a5 (TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R2, offset_rw, TCG_REG_R2), -#if TARGET_LONG_BITS == 32 - tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R57, addr_reg), -#else - tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R57, - 0, addr_reg), -#endif + tcg_opc_ext_i(TCG_REG_P0, + TARGET_LONG_BITS == 32 ? MO_UL : MO_Q, + TCG_REG_R57, addr_reg), tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, TCG_REG_R2, TCG_AREG0)); tcg_out_bundle(s, mII, @@ -1538,23 +1601,20 @@ static const void * const qemu_ld_helpers[4] = { helper_ldq_mmu, }; -static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, + TCGMemOp opc) { - int addr_reg, data_reg, mem_index, s_bits, bswap; - uint64_t opc_ld_m1[4] = { OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 }; - uint64_t opc_ext_i29[8] = { OPC_ZXT1_I29, OPC_ZXT2_I29, OPC_ZXT4_I29, 0, - OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0 }; + static const uint64_t opc_ld_m1[4] = { + OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 + }; + int addr_reg, data_reg, mem_index; + TCGMemOp s_bits, bswap; data_reg = *args++; addr_reg = *args++; mem_index = *args; - s_bits = opc & 3; - -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 1; -#else - bswap = 0; -#endif + s_bits = opc & MO_SIZE; + bswap = opc & MO_BSWAP; /* Read the TLB entry */ tcg_out_qemu_tlb(s, addr_reg, s_bits, @@ -1563,8 +1623,7 @@ static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) /* P6 is the fast path, and P7 the slow path */ tcg_out_bundle(s, mLX, - tcg_opc_a4 (TCG_REG_P7, OPC_ADDS_A4, - TCG_REG_R56, 0, TCG_AREG0), + tcg_opc_mov_a(TCG_REG_P7, TCG_REG_R56, TCG_AREG0), tcg_opc_l2 ((tcg_target_long) qemu_ld_helpers[s_bits]), tcg_opc_x2 (TCG_REG_P7, OPC_MOVL_X2, TCG_REG_R2, (tcg_target_long) qemu_ld_helpers[s_bits])); @@ -1575,14 +1634,14 @@ static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) TCG_REG_R3, TCG_REG_R57), tcg_opc_i21(TCG_REG_P7, OPC_MOV_I21, TCG_REG_B6, TCG_REG_R3, 0)); - if (bswap && s_bits == 1) { + if (bswap && s_bits == MO_16) { tcg_out_bundle(s, MmI, tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], TCG_REG_R8, TCG_REG_R3), tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, TCG_REG_R8, TCG_REG_R8, 15, 15)); - } else if (bswap && s_bits == 2) { + } else if (bswap && s_bits == MO_32) { tcg_out_bundle(s, MmI, tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], TCG_REG_R8, TCG_REG_R3), @@ -1594,38 +1653,26 @@ static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], TCG_REG_R8, TCG_REG_R3), tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); } - if (!bswap || s_bits == 0) { + if (!bswap) { tcg_out_bundle(s, miB, - tcg_opc_a5 (TCG_REG_P7, OPC_ADDL_A5, TCG_REG_R58, - mem_index, TCG_REG_R0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_movi_a(TCG_REG_P7, TCG_REG_R58, mem_index), + INSN_NOP_I, tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5, TCG_REG_B0, TCG_REG_B6)); } else { tcg_out_bundle(s, miB, - tcg_opc_a5 (TCG_REG_P7, OPC_ADDL_A5, TCG_REG_R58, - mem_index, TCG_REG_R0), - tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, - TCG_REG_R8, TCG_REG_R8, 0xb), + tcg_opc_movi_a(TCG_REG_P7, TCG_REG_R58, mem_index), + tcg_opc_bswap64_i(TCG_REG_P6, TCG_REG_R8, TCG_REG_R8), tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5, TCG_REG_B0, TCG_REG_B6)); } - if (opc == 3) { - tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, - data_reg, 0, TCG_REG_R8)); - } else { - tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i29(TCG_REG_P0, opc_ext_i29[opc], - data_reg, TCG_REG_R8)); - } + tcg_out_bundle(s, miI, + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, TCG_REG_R8)); } /* helper signature: helper_st_mmu(CPUState *env, target_ulong addr, @@ -1637,32 +1684,30 @@ static const void * const qemu_st_helpers[4] = { helper_stq_mmu, }; -static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, + TCGMemOp opc) { - int addr_reg, data_reg, mem_index, bswap; - uint64_t opc_st_m4[4] = { OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 }; + static const uint64_t opc_st_m4[4] = { + OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 + }; + int addr_reg, data_reg, mem_index; + TCGMemOp s_bits; data_reg = *args++; addr_reg = *args++; mem_index = *args; + s_bits = opc & MO_SIZE; -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 1; -#else - bswap = 0; -#endif - - tcg_out_qemu_tlb(s, addr_reg, opc, + tcg_out_qemu_tlb(s, addr_reg, s_bits, offsetof(CPUArchState, tlb_table[mem_index][0].addr_write), offsetof(CPUArchState, tlb_table[mem_index][0].addend)); /* P6 is the fast path, and P7 the slow path */ tcg_out_bundle(s, mLX, - tcg_opc_a4 (TCG_REG_P7, OPC_ADDS_A4, - TCG_REG_R56, 0, TCG_AREG0), - tcg_opc_l2 ((tcg_target_long) qemu_st_helpers[opc]), + tcg_opc_mov_a(TCG_REG_P7, TCG_REG_R56, TCG_AREG0), + tcg_opc_l2 ((tcg_target_long) qemu_st_helpers[s_bits]), tcg_opc_x2 (TCG_REG_P7, OPC_MOVL_X2, TCG_REG_R2, - (tcg_target_long) qemu_st_helpers[opc])); + (tcg_target_long) qemu_st_helpers[s_bits])); tcg_out_bundle(s, MmI, tcg_opc_m3 (TCG_REG_P0, OPC_LD8_M3, TCG_REG_R3, TCG_REG_R2, 8), @@ -1671,150 +1716,145 @@ static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) tcg_opc_i21(TCG_REG_P7, OPC_MOV_I21, TCG_REG_B6, TCG_REG_R3, 0)); - if (!bswap || opc == 0) { + switch (opc) { + case MO_8: + case MO_16: + case MO_32: + case MO_64: tcg_out_bundle(s, mii, tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), - tcg_opc_a4 (TCG_REG_P7, OPC_ADDS_A4, TCG_REG_R58, - 0, data_reg), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); - } else if (opc == 1) { + tcg_opc_mov_a(TCG_REG_P7, TCG_REG_R58, data_reg), + INSN_NOP_I); + break; + + case MO_16 | MO_BSWAP: tcg_out_bundle(s, miI, tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_I, tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, TCG_REG_R2, data_reg, 15, 15)); tcg_out_bundle(s, miI, - tcg_opc_a4 (TCG_REG_P7, OPC_ADDS_A4, TCG_REG_R58, - 0, data_reg), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, - TCG_REG_R2, TCG_REG_R2, 0xb)); + tcg_opc_mov_a(TCG_REG_P7, TCG_REG_R58, data_reg), + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P6, TCG_REG_R2, TCG_REG_R2)); data_reg = TCG_REG_R2; - } else if (opc == 2) { + break; + + case MO_32 | MO_BSWAP: tcg_out_bundle(s, miI, tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_I, tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, TCG_REG_R2, data_reg, 31, 31)); tcg_out_bundle(s, miI, - tcg_opc_a4 (TCG_REG_P7, OPC_ADDS_A4, TCG_REG_R58, - 0, data_reg), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, - TCG_REG_R2, TCG_REG_R2, 0xb)); + tcg_opc_mov_a(TCG_REG_P7, TCG_REG_R58, data_reg), + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P6, TCG_REG_R2, TCG_REG_R2)); data_reg = TCG_REG_R2; - } else if (opc == 3) { + break; + + case MO_64 | MO_BSWAP: tcg_out_bundle(s, miI, tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), - tcg_opc_a4 (TCG_REG_P7, OPC_ADDS_A4, TCG_REG_R58, - 0, data_reg), - tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, - TCG_REG_R2, data_reg, 0xb)); + tcg_opc_mov_a(TCG_REG_P7, TCG_REG_R58, data_reg), + tcg_opc_bswap64_i(TCG_REG_P6, TCG_REG_R2, data_reg)); data_reg = TCG_REG_R2; + break; + + default: + tcg_abort(); } tcg_out_bundle(s, miB, - tcg_opc_m4 (TCG_REG_P6, opc_st_m4[opc], + tcg_opc_m4 (TCG_REG_P6, opc_st_m4[s_bits], data_reg, TCG_REG_R3), - tcg_opc_a5 (TCG_REG_P7, OPC_ADDL_A5, TCG_REG_R59, - mem_index, TCG_REG_R0), + tcg_opc_movi_a(TCG_REG_P7, TCG_REG_R59, mem_index), tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5, TCG_REG_B0, TCG_REG_B6)); } #else /* !CONFIG_SOFTMMU */ -static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, + TCGMemOp opc) { static uint64_t const opc_ld_m1[4] = { OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 }; - static uint64_t const opc_sxt_i29[4] = { - OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0 - }; - int addr_reg, data_reg, s_bits, bswap; + int addr_reg, data_reg; + TCGMemOp s_bits, bswap; data_reg = *args++; addr_reg = *args++; - s_bits = opc & 3; - -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 1; -#else - bswap = 0; -#endif + s_bits = opc & MO_SIZE; + bswap = opc & MO_BSWAP; #if TARGET_LONG_BITS == 32 if (GUEST_BASE != 0) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R3, addr_reg), tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, TCG_GUEST_BASE_REG, TCG_REG_R3)); } else { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R2, addr_reg), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); } - if (!bswap || s_bits == 0) { - if (s_bits == opc) { + if (!bswap) { + if (!(opc & MO_SIGN)) { tcg_out_bundle(s, miI, tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], data_reg, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I, + INSN_NOP_I); } else { tcg_out_bundle(s, mII, tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], data_reg, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits], - data_reg, data_reg)); + INSN_NOP_I, + tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, data_reg)); } - } else if (s_bits == 3) { + } else if (s_bits == MO_64) { tcg_out_bundle(s, mII, tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], data_reg, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - data_reg, data_reg, 0xb)); + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); } else { - if (s_bits == 1) { + if (s_bits == MO_16) { tcg_out_bundle(s, mII, tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], data_reg, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_I, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, data_reg, data_reg, 15, 15)); } else { tcg_out_bundle(s, mII, tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], data_reg, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + INSN_NOP_I, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, data_reg, data_reg, 31, 31)); } - if (opc == s_bits) { + if (!(opc & MO_SIGN)) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - data_reg, data_reg, 0xb)); + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); } else { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - data_reg, data_reg, 0xb), - tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits], - data_reg, data_reg)); + INSN_NOP_M, + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg), + tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, data_reg)); } } #else @@ -1824,157 +1864,149 @@ static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) TCG_GUEST_BASE_REG, addr_reg), tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], data_reg, TCG_REG_R2), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); } else { tcg_out_bundle(s, mmI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], data_reg, addr_reg), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); } - if (bswap && s_bits == 1) { + if (bswap && s_bits == MO_16) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, data_reg, data_reg, 15, 15), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - data_reg, data_reg, 0xb)); - } else if (bswap && s_bits == 2) { + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); + } else if (bswap && s_bits == MO_32) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, data_reg, data_reg, 31, 31), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - data_reg, data_reg, 0xb)); - } else if (bswap && s_bits == 3) { + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); + } else if (bswap && s_bits == MO_64) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - data_reg, data_reg, 0xb)); + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, data_reg, data_reg)); } - if (s_bits != opc) { + if (opc & MO_SIGN) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits], - data_reg, data_reg)); + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_ext_i(TCG_REG_P0, opc, data_reg, data_reg)); } #endif } -static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, + TCGMemOp opc) { static uint64_t const opc_st_m4[4] = { OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 }; - int addr_reg, data_reg, bswap; + int addr_reg, data_reg; #if TARGET_LONG_BITS == 64 uint64_t add_guest_base; #endif + TCGMemOp s_bits, bswap; data_reg = *args++; addr_reg = *args++; - -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 1; -#else - bswap = 0; -#endif + s_bits = opc & MO_SIZE; + bswap = opc & MO_BSWAP; #if TARGET_LONG_BITS == 32 if (GUEST_BASE != 0) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R3, addr_reg), tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, TCG_GUEST_BASE_REG, TCG_REG_R3)); } else { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R2, addr_reg), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); } if (bswap) { - if (opc == 1) { + if (s_bits == MO_16) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, TCG_REG_R3, data_reg, 15, 15), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - TCG_REG_R3, TCG_REG_R3, 0xb)); + tcg_opc_bswap64_i(TCG_REG_P0, + TCG_REG_R3, TCG_REG_R3)); data_reg = TCG_REG_R3; - } else if (opc == 2) { + } else if (s_bits == MO_32) { tcg_out_bundle(s, mII, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, TCG_REG_R3, data_reg, 31, 31), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - TCG_REG_R3, TCG_REG_R3, 0xb)); + tcg_opc_bswap64_i(TCG_REG_P0, + TCG_REG_R3, TCG_REG_R3)); data_reg = TCG_REG_R3; - } else if (opc == 3) { + } else if (s_bits == MO_64) { tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - TCG_REG_R3, data_reg, 0xb)); + INSN_NOP_M, + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, TCG_REG_R3, data_reg)); data_reg = TCG_REG_R3; } } tcg_out_bundle(s, mmI, - tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc], + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[s_bits], data_reg, TCG_REG_R2), - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_M, + INSN_NOP_I); #else if (GUEST_BASE != 0) { add_guest_base = tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, TCG_GUEST_BASE_REG, addr_reg); addr_reg = TCG_REG_R2; } else { - add_guest_base = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); + add_guest_base = INSN_NOP_M; } - if (!bswap || opc == 0) { + if (!bswap) { tcg_out_bundle(s, (GUEST_BASE ? MmI : mmI), add_guest_base, - tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc], + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[s_bits], data_reg, addr_reg), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I); } else { - if (opc == 1) { + if (s_bits == MO_16) { tcg_out_bundle(s, mII, add_guest_base, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, TCG_REG_R3, data_reg, 15, 15), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - TCG_REG_R3, TCG_REG_R3, 0xb)); + tcg_opc_bswap64_i(TCG_REG_P0, + TCG_REG_R3, TCG_REG_R3)); data_reg = TCG_REG_R3; - } else if (opc == 2) { + } else if (s_bits == MO_32) { tcg_out_bundle(s, mII, add_guest_base, tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, TCG_REG_R3, data_reg, 31, 31), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - TCG_REG_R3, TCG_REG_R3, 0xb)); + tcg_opc_bswap64_i(TCG_REG_P0, + TCG_REG_R3, TCG_REG_R3)); data_reg = TCG_REG_R3; - } else if (opc == 3) { + } else if (s_bits == MO_64) { tcg_out_bundle(s, miI, add_guest_base, - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, - TCG_REG_R3, data_reg, 0xb)); + INSN_NOP_I, + tcg_opc_bswap64_i(TCG_REG_P0, TCG_REG_R3, data_reg)); data_reg = TCG_REG_R3; } tcg_out_bundle(s, miI, - tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc], + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[s_bits], data_reg, addr_reg), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), - tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + INSN_NOP_I, + INSN_NOP_I); } #endif } @@ -1992,7 +2024,11 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_br(s, args[0]); break; case INDEX_op_call: - tcg_out_call(s, args[0]); + if (likely(const_args[0])) { + tcg_out_calli(s, args[0]); + } else { + tcg_out_callr(s, args[0]); + } break; case INDEX_op_goto_tb: tcg_out_goto_tb(s, args[0]); @@ -2052,24 +2088,23 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, case INDEX_op_add_i32: case INDEX_op_add_i64: - tcg_out_alu(s, OPC_ADD_A1, args[0], args[1], const_args[1], - args[2], const_args[2]); + tcg_out_add(s, args[0], args[1], args[2], const_args[2]); break; case INDEX_op_sub_i32: case INDEX_op_sub_i64: - tcg_out_alu(s, OPC_SUB_A1, args[0], args[1], const_args[1], - args[2], const_args[2]); + tcg_out_sub(s, args[0], args[1], const_args[1], args[2], const_args[2]); break; case INDEX_op_and_i32: case INDEX_op_and_i64: - tcg_out_alu(s, OPC_AND_A1, args[0], args[1], const_args[1], - args[2], const_args[2]); + /* TCG expects arg2 constant; A3 expects arg1 constant. Swap. */ + tcg_out_alu(s, OPC_AND_A1, OPC_AND_A3, args[0], + args[2], const_args[2], args[1], const_args[1]); break; case INDEX_op_andc_i32: case INDEX_op_andc_i64: - tcg_out_alu(s, OPC_ANDCM_A1, args[0], args[1], const_args[1], - args[2], const_args[2]); + tcg_out_alu(s, OPC_ANDCM_A1, OPC_ANDCM_A3, args[0], + args[1], const_args[1], args[2], const_args[2]); break; case INDEX_op_eqv_i32: case INDEX_op_eqv_i64: @@ -2088,8 +2123,9 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_or_i32: case INDEX_op_or_i64: - tcg_out_alu(s, OPC_OR_A1, args[0], args[1], const_args[1], - args[2], const_args[2]); + /* TCG expects arg2 constant; A3 expects arg1 constant. Swap. */ + tcg_out_alu(s, OPC_OR_A1, OPC_OR_A3, args[0], + args[2], const_args[2], args[1], const_args[1]); break; case INDEX_op_orc_i32: case INDEX_op_orc_i64: @@ -2098,8 +2134,9 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_xor_i32: case INDEX_op_xor_i64: - tcg_out_alu(s, OPC_XOR_A1, args[0], args[1], const_args[1], - args[2], const_args[2]); + /* TCG expects arg2 constant; A3 expects arg1 constant. Swap. */ + tcg_out_alu(s, OPC_XOR_A1, OPC_XOR_A3, args[0], + args[2], const_args[2], args[1], const_args[1]); break; case INDEX_op_mul_i32: @@ -2180,12 +2217,10 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_brcond_i32: - tcg_out_brcond(s, args[2], args[0], const_args[0], - args[1], const_args[1], args[3], 1); + tcg_out_brcond(s, args[2], args[0], args[1], args[3], 1); break; case INDEX_op_brcond_i64: - tcg_out_brcond(s, args[2], args[0], const_args[0], - args[1], const_args[1], args[3], 0); + tcg_out_brcond(s, args[2], args[0], args[1], args[3], 0); break; case INDEX_op_setcond_i32: tcg_out_setcond(s, args[3], args[0], args[1], args[2], 1); @@ -2203,39 +2238,39 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_qemu_ld8u: - tcg_out_qemu_ld(s, args, 0); + tcg_out_qemu_ld(s, args, MO_UB); break; case INDEX_op_qemu_ld8s: - tcg_out_qemu_ld(s, args, 0 | 4); + tcg_out_qemu_ld(s, args, MO_SB); break; case INDEX_op_qemu_ld16u: - tcg_out_qemu_ld(s, args, 1); + tcg_out_qemu_ld(s, args, MO_TEUW); break; case INDEX_op_qemu_ld16s: - tcg_out_qemu_ld(s, args, 1 | 4); + tcg_out_qemu_ld(s, args, MO_TESW); break; case INDEX_op_qemu_ld32: case INDEX_op_qemu_ld32u: - tcg_out_qemu_ld(s, args, 2); + tcg_out_qemu_ld(s, args, MO_TEUL); break; case INDEX_op_qemu_ld32s: - tcg_out_qemu_ld(s, args, 2 | 4); + tcg_out_qemu_ld(s, args, MO_TESL); break; case INDEX_op_qemu_ld64: - tcg_out_qemu_ld(s, args, 3); + tcg_out_qemu_ld(s, args, MO_TEQ); break; case INDEX_op_qemu_st8: - tcg_out_qemu_st(s, args, 0); + tcg_out_qemu_st(s, args, MO_UB); break; case INDEX_op_qemu_st16: - tcg_out_qemu_st(s, args, 1); + tcg_out_qemu_st(s, args, MO_TEUW); break; case INDEX_op_qemu_st32: - tcg_out_qemu_st(s, args, 2); + tcg_out_qemu_st(s, args, MO_TEUL); break; case INDEX_op_qemu_st64: - tcg_out_qemu_st(s, args, 3); + tcg_out_qemu_st(s, args, MO_TEQ); break; default: @@ -2245,7 +2280,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, static const TCGTargetOpDef ia64_op_defs[] = { { INDEX_op_br, { } }, - { INDEX_op_call, { "r" } }, + { INDEX_op_call, { "ri" } }, { INDEX_op_exit_tb, { } }, { INDEX_op_goto_tb, { } }, @@ -2261,7 +2296,7 @@ static const TCGTargetOpDef ia64_op_defs[] = { { INDEX_op_st16_i32, { "rZ", "r" } }, { INDEX_op_st_i32, { "rZ", "r" } }, - { INDEX_op_add_i32, { "r", "rI", "rI" } }, + { INDEX_op_add_i32, { "r", "rZ", "rI" } }, { INDEX_op_sub_i32, { "r", "rI", "rI" } }, { INDEX_op_and_i32, { "r", "rI", "rI" } }, @@ -2289,7 +2324,7 @@ static const TCGTargetOpDef ia64_op_defs[] = { { INDEX_op_bswap16_i32, { "r", "rZ" } }, { INDEX_op_bswap32_i32, { "r", "rZ" } }, - { INDEX_op_brcond_i32, { "rI", "rI" } }, + { INDEX_op_brcond_i32, { "rZ", "rZ" } }, { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } }, { INDEX_op_movcond_i32, { "r", "rZ", "rZ", "rI", "rI" } }, @@ -2308,7 +2343,7 @@ static const TCGTargetOpDef ia64_op_defs[] = { { INDEX_op_st32_i64, { "rZ", "r" } }, { INDEX_op_st_i64, { "rZ", "r" } }, - { INDEX_op_add_i64, { "r", "rI", "rI" } }, + { INDEX_op_add_i64, { "r", "rZ", "rI" } }, { INDEX_op_sub_i64, { "r", "rI", "rI" } }, { INDEX_op_and_i64, { "r", "rI", "rI" } }, @@ -2339,7 +2374,7 @@ static const TCGTargetOpDef ia64_op_defs[] = { { INDEX_op_bswap32_i64, { "r", "rZ" } }, { INDEX_op_bswap64_i64, { "r", "rZ" } }, - { INDEX_op_brcond_i64, { "rI", "rI" } }, + { INDEX_op_brcond_i64, { "rZ", "rZ" } }, { INDEX_op_setcond_i64, { "r", "rZ", "rZ" } }, { INDEX_op_movcond_i64, { "r", "rZ", "rZ", "rI", "rI" } }, @@ -2384,8 +2419,7 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_bundle(s, miI, tcg_opc_m34(TCG_REG_P0, OPC_ALLOC_M34, TCG_REG_R34, 32, 24, 0), - tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, - TCG_AREG0, 0, TCG_REG_R32), + INSN_NOP_I, tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, TCG_REG_R33, 0)); @@ -2393,7 +2427,7 @@ static void tcg_target_qemu_prologue(TCGContext *s) an ADDL in the M slot of the next bundle. */ if (GUEST_BASE != 0) { tcg_out_bundle(s, mlx, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_l2 (GUEST_BASE), tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, TCG_GUEST_BASE_REG, GUEST_BASE)); @@ -2404,19 +2438,19 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R12, -frame_size, TCG_REG_R12), tcg_opc_i22(TCG_REG_P0, OPC_MOV_I22, - TCG_REG_R32, TCG_REG_B0), + TCG_REG_R33, TCG_REG_B0), tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); /* epilogue */ tb_ret_addr = s->code_ptr; tcg_out_bundle(s, miI, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, - TCG_REG_B0, TCG_REG_R32, 0), + TCG_REG_B0, TCG_REG_R33, 0), tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R12, frame_size, TCG_REG_R12)); tcg_out_bundle(s, miB, - tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + INSN_NOP_M, tcg_opc_i26(TCG_REG_P0, OPC_MOV_I_I26, TCG_REG_PFS, TCG_REG_R34), tcg_opc_b4 (TCG_REG_P0, OPC_BR_RET_SPTK_MANY_B4, @@ -2469,16 +2503,17 @@ static void tcg_target_init(TCGContext *s) tcg_regset_set_reg(s->reserved_regs, TCG_REG_R3); /* internal use */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R12); /* stack pointer */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R32); /* return address */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R33); /* return address */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R34); /* PFS */ - /* The following 3 are not in use, are call-saved, but *not* saved + /* The following 4 are not in use, are call-saved, but *not* saved by the prologue. Therefore we cannot use them without modifying the prologue. There doesn't seem to be any good reason to use these as opposed to the windowed registers. */ tcg_regset_set_reg(s->reserved_regs, TCG_REG_R4); tcg_regset_set_reg(s->reserved_regs, TCG_REG_R5); tcg_regset_set_reg(s->reserved_regs, TCG_REG_R6); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R7); tcg_add_target_add_op_defs(ia64_op_defs); } diff --git a/tcg/ia64/tcg-target.h b/tcg/ia64/tcg-target.h index c90038aae5..52a939c946 100644 --- a/tcg/ia64/tcg-target.h +++ b/tcg/ia64/tcg-target.h @@ -92,6 +92,8 @@ typedef enum { TCG_REG_R61, TCG_REG_R62, TCG_REG_R63, + + TCG_AREG0 = TCG_REG_R32, } TCGReg; #define TCG_CT_CONST_ZERO 0x100 @@ -162,8 +164,6 @@ typedef enum { #define TCG_TARGET_HAS_not_i32 0 /* xor r1, -1, r3 */ #define TCG_TARGET_HAS_not_i64 0 /* xor r1, -1, r3 */ -#define TCG_AREG0 TCG_REG_R7 - static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { start = start & ~(32UL - 1UL); diff --git a/tests/Makefile b/tests/Makefile index fa4c9f0cbb..379cdd9ad1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -67,25 +67,52 @@ check-qtest-i386-y += tests/boot-order-test$(EXESUF) check-qtest-i386-y += tests/rtc-test$(EXESUF) check-qtest-i386-y += tests/i440fx-test$(EXESUF) check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) +check-qtest-i386-y += tests/qom-test$(EXESUF) +check-qtest-i386-y += tests/blockdev-test$(EXESUF) +check-qtest-i386-y += tests/qdev-monitor-test$(EXESUF) check-qtest-x86_64-y = $(check-qtest-i386-y) gcov-files-i386-y += i386-softmmu/hw/mc146818rtc.c gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y)) check-qtest-mips-y = tests/endianness-test$(EXESUF) check-qtest-mips64-y = tests/endianness-test$(EXESUF) check-qtest-mips64el-y = tests/endianness-test$(EXESUF) +check-qtest-mips-y += tests/qom-test$(EXESUF) +check-qtest-mipsel-y += tests/qom-test$(EXESUF) +check-qtest-mips64-y += tests/qom-test$(EXESUF) +check-qtest-mips64el-y += tests/qom-test$(EXESUF) check-qtest-ppc-y = tests/endianness-test$(EXESUF) check-qtest-ppc64-y = tests/endianness-test$(EXESUF) check-qtest-sh4-y = tests/endianness-test$(EXESUF) check-qtest-sh4eb-y = tests/endianness-test$(EXESUF) +check-qtest-sh4-y += tests/qom-test$(EXESUF) +check-qtest-sh4eb-y += tests/qom-test$(EXESUF) check-qtest-sparc64-y = tests/endianness-test$(EXESUF) #check-qtest-sparc-y = tests/m48t59-test$(EXESUF) #check-qtest-sparc64-y += tests/m48t59-test$(EXESUF) gcov-files-sparc-y += hw/m48t59.c gcov-files-sparc64-y += hw/m48t59.c +check-qtest-sparc-y += tests/qom-test$(EXESUF) +check-qtest-sparc64-y += tests/qom-test$(EXESUF) check-qtest-arm-y = tests/tmp105-test$(EXESUF) gcov-files-arm-y += hw/tmp105.c +check-qtest-arm-y += tests/qom-test$(EXESUF) check-qtest-ppc-y += tests/boot-order-test$(EXESUF) check-qtest-ppc64-y += tests/boot-order-test$(EXESUF) +check-qtest-ppc-y += tests/qom-test$(EXESUF) +check-qtest-ppc64-y += tests/qom-test$(EXESUF) +check-qtest-ppcemb-y += tests/qom-test$(EXESUF) +check-qtest-alpha-y += tests/qom-test$(EXESUF) +check-qtest-cris-y += tests/qom-test$(EXESUF) +check-qtest-lm32-y += tests/qom-test$(EXESUF) +check-qtest-m68k-y += tests/qom-test$(EXESUF) +check-qtest-microblaze-y += tests/qom-test$(EXESUF) +check-qtest-microblazeel-y = $(check-qtest-microblaze-y) +check-qtest-moxie-y += tests/qom-test$(EXESUF) +check-qtest-or32-y += tests/qom-test$(EXESUF) +check-qtest-s390x-y += tests/qom-test$(EXESUF) +check-qtest-unicore32-y += tests/qom-test$(EXESUF) +check-qtest-xtensa-y += tests/qom-test$(EXESUF) +check-qtest-xtensaeb-y = $(check-qtest-xtensa-y) check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ comments.json empty.json funny-char.json indented-expr.json \ @@ -174,6 +201,9 @@ tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) +tests/qom-test$(EXESUF): tests/qom-test.o +tests/blockdev-test$(EXESUF): tests/blockdev-test.o $(libqos-pc-obj-y) +tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y) tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o # QTest rules diff --git a/tests/blockdev-test.c b/tests/blockdev-test.c new file mode 100644 index 0000000000..c940e00690 --- /dev/null +++ b/tests/blockdev-test.c @@ -0,0 +1,59 @@ +/* + * blockdev.c test cases + * + * Copyright (C) 2013 Red Hat Inc. + * + * Authors: + * Stefan Hajnoczi <stefanha@redhat.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include <glib.h> +#include <string.h> +#include "libqtest.h" + +static void test_drive_add_empty(void) +{ + QDict *response; + const char *response_return; + + /* Start with an empty drive */ + qtest_start("-drive if=none,id=drive0"); + + /* Delete the drive */ + response = qmp("{\"execute\": \"human-monitor-command\"," + " \"arguments\": {" + " \"command-line\": \"drive_del drive0\"" + "}}"); + g_assert(response); + response_return = qdict_get_try_str(response, "return"); + g_assert(response_return); + g_assert(strcmp(response_return, "") == 0); + QDECREF(response); + + /* Ensure re-adding the drive works - there should be no duplicate ID error + * because the old drive must be gone. + */ + response = qmp("{\"execute\": \"human-monitor-command\"," + " \"arguments\": {" + " \"command-line\": \"drive_add 0 if=none,id=drive0\"" + "}}"); + g_assert(response); + response_return = qdict_get_try_str(response, "return"); + g_assert(response_return); + g_assert(strcmp(response_return, "OK\r\n") == 0); + QDECREF(response); + + qtest_end(); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/qmp/drive_add_empty", test_drive_add_empty); + + return g_test_run(); +} diff --git a/tests/boot-order-test.c b/tests/boot-order-test.c index 4b233d0b24..360a6911eb 100644 --- a/tests/boot-order-test.c +++ b/tests/boot-order-test.c @@ -34,19 +34,19 @@ static void test_a_boot_order(const char *machine, char *args; uint64_t actual; - args = g_strdup_printf("-nodefaults -display none%s%s %s", + args = g_strdup_printf("-nodefaults%s%s %s", machine ? " -M " : "", machine ?: "", test_args); qtest_start(args); actual = read_boot_order(); g_assert_cmphex(actual, ==, expected_boot); - qmp("{ 'execute': 'system_reset' }"); + qmp_discard_response("{ 'execute': 'system_reset' }"); /* * system_reset only requests reset. We get a RESET event after * the actual reset completes. Need to wait for that. */ - qmp(""); /* HACK: wait for event */ + qmp_discard_response(""); /* HACK: wait for event */ actual = read_boot_order(); g_assert_cmphex(actual, ==, expected_reboot); qtest_quit(global_qtest); diff --git a/tests/endianness-test.c b/tests/endianness-test.c index feb32a8503..646df7d8da 100644 --- a/tests/endianness-test.c +++ b/tests/endianness-test.c @@ -44,7 +44,8 @@ static const TestCase test_cases[] = { { "ppc", "prep", 0x80000000, .bswap = true }, { "ppc", "bamboo", 0xe8000000, .bswap = true, .superio = "i82378" }, { "ppc64", "mac99", 0xf2000000, .bswap = true, .superio = "i82378" }, - { "ppc64", "pseries", 0x10080000000, .bswap = true, .superio = "i82378" }, + { "ppc64", "pseries", 0x10080000000ULL, + .bswap = true, .superio = "i82378" }, { "sh4", "r2d", 0xfe240000, .superio = "i82378" }, { "sh4eb", "r2d", 0xfe240000, .bswap = true, .superio = "i82378" }, { "sparc64", "sun4u", 0x1fe02000000LL, .bswap = true }, @@ -120,7 +121,7 @@ static void test_endianness(gconstpointer data) const TestCase *test = data; char *args; - args = g_strdup_printf("-display none -M %s%s%s -device pc-testdev", + args = g_strdup_printf("-M %s%s%s -device pc-testdev", test->machine, test->superio ? " -device " : "", test->superio ?: ""); @@ -195,7 +196,7 @@ static void test_endianness_split(gconstpointer data) const TestCase *test = data; char *args; - args = g_strdup_printf("-display none -M %s%s%s -device pc-testdev", + args = g_strdup_printf("-M %s%s%s -device pc-testdev", test->machine, test->superio ? " -device " : "", test->superio ?: ""); @@ -242,7 +243,7 @@ static void test_endianness_combine(gconstpointer data) const TestCase *test = data; char *args; - args = g_strdup_printf("-display none -M %s%s%s -device pc-testdev", + args = g_strdup_printf("-M %s%s%s -device pc-testdev", test->machine, test->superio ? " -device " : "", test->superio ?: ""); diff --git a/tests/fdc-test.c b/tests/fdc-test.c index fd198dcf8b..38b5b178d0 100644 --- a/tests/fdc-test.c +++ b/tests/fdc-test.c @@ -290,10 +290,12 @@ static void test_media_insert(void) /* Insert media in drive. DSKCHK should not be reset until a step pulse * is sent. */ - qmp("{'execute':'change', 'arguments':{ 'device':'floppy0', " - "'target': '%s' }}", test_image); - qmp(""); /* ignore event (FIXME open -> open transition?!) */ - qmp(""); /* ignore event */ + qmp_discard_response("{'execute':'change', 'arguments':{" + " 'device':'floppy0', 'target': '%s' }}", + test_image); + qmp_discard_response(""); /* ignore event + (FIXME open -> open transition?!) */ + qmp_discard_response(""); /* ignore event */ dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); @@ -322,8 +324,9 @@ static void test_media_change(void) /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't * reset the bit. */ - qmp("{'execute':'eject', 'arguments':{ 'device':'floppy0' }}"); - qmp(""); /* ignore event */ + qmp_discard_response("{'execute':'eject', 'arguments':{" + " 'device':'floppy0' }}"); + qmp_discard_response(""); /* ignore event */ dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); diff --git a/tests/fw_cfg-test.c b/tests/fw_cfg-test.c index b86e49ab09..e4f355ce3f 100644 --- a/tests/fw_cfg-test.c +++ b/tests/fw_cfg-test.c @@ -126,8 +126,7 @@ int main(int argc, char **argv) g_test_add_func("/fw_cfg/numa", test_fw_cfg_numa); g_test_add_func("/fw_cfg/boot_menu", test_fw_cfg_boot_menu); - cmdline = g_strdup_printf("-display none " - "-uuid 4600cb32-38ec-4b2f-8acb-81c6ea54f2d8 "); + cmdline = g_strdup_printf("-uuid 4600cb32-38ec-4b2f-8acb-81c6ea54f2d8 "); s = qtest_start(cmdline); g_free(cmdline); diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c index b72042e59d..c84d1e75e0 100644 --- a/tests/hd-geo-test.c +++ b/tests/hd-geo-test.c @@ -171,7 +171,7 @@ static int setup_common(char *argv[], int argv_sz) { memset(cur_ide, 0, sizeof(cur_ide)); return append_arg(0, argv, argv_sz, - g_strdup("-nodefaults -display none")); + g_strdup("-nodefaults")); } static void setup_mbr(int img_idx, MBRcontents mbr) diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c index 08ce820ebd..65c786ca1e 100644 --- a/tests/i440fx-test.c +++ b/tests/i440fx-test.c @@ -265,7 +265,7 @@ int main(int argc, char **argv) data.num_cpus = 1; - cmdline = g_strdup_printf("-display none -smp %d", data.num_cpus); + cmdline = g_strdup_printf("-smp %d", data.num_cpus); s = qtest_start(cmdline); g_free(cmdline); diff --git a/tests/ide-test.c b/tests/ide-test.c index 7307f1d336..d5cec5a1fc 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -81,6 +81,7 @@ enum { CMD_IDENTIFY = 0xec, CMDF_ABORT = 0x100, + CMDF_NO_BM = 0x200, }; enum { @@ -192,6 +193,11 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, g_assert_not_reached(); } + if (flags & CMDF_NO_BM) { + qpci_config_writew(dev, PCI_COMMAND, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY); + } + /* Select device 0 */ outb(IDE_BASE + reg_device, 0 | LBA); @@ -352,6 +358,25 @@ static void test_bmdma_long_prdt(void) assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); } +static void test_bmdma_no_busmaster(void) +{ + uint8_t status; + + /* 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 + * good at confusing and occasionally crashing qemu. */ + PrdtEntry prdt[4096] = { }; + + status = send_dma_request(CMD_READ_DMA | CMDF_NO_BM, 0, 512, + prdt, ARRAY_SIZE(prdt)); + + /* 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); +} + static void test_bmdma_setup(void) { ide_test_start( @@ -435,8 +460,9 @@ static void test_flush(void) tmp_path); /* Delay the completion of the flush request until we explicitly do it */ - qmp("{'execute':'human-monitor-command', 'arguments': { " - "'command-line': 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }"); + qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {" + " 'command-line':" + " 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }"); /* FLUSH CACHE command on device 0*/ outb(IDE_BASE + reg_device, 0); @@ -448,8 +474,9 @@ static void test_flush(void) assert_bit_clear(data, DF | ERR | DRQ); /* Complete the command */ - qmp("{'execute':'human-monitor-command', 'arguments': { " - "'command-line': 'qemu-io ide0-hd0 \"resume A\"'} }"); + qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {" + " 'command-line':" + " 'qemu-io ide0-hd0 \"resume A\"'} }"); /* Check registers */ data = inb(IDE_BASE + reg_device); @@ -493,6 +520,7 @@ int main(int argc, char **argv) qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw); qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt); qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt); + qtest_add_func("/ide/bmdma/no_busmaster", test_bmdma_no_busmaster); qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown); qtest_add_func("/ide/flush", test_flush); diff --git a/tests/libqtest.c b/tests/libqtest.c index bb82069f5c..359d571a06 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -30,6 +30,8 @@ #include "qemu/compiler.h" #include "qemu/osdep.h" +#include "qapi/qmp/json-streamer.h" +#include "qapi/qmp/json-parser.h" #define MAX_IRQ 256 @@ -133,6 +135,7 @@ QTestState *qtest_init(const char *extra_args) "-qmp unix:%s,nowait " "-pidfile %s " "-machine accel=qtest " + "-display none " "%s", qemu_binary, s->socket_path, s->qmp_socket_path, pid_file, extra_args ?: ""); @@ -151,8 +154,8 @@ QTestState *qtest_init(const char *extra_args) } /* Read the QMP greeting and then do the handshake */ - qtest_qmp(s, ""); - qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }"); + qtest_qmp_discard_response(s, ""); + qtest_qmp_discard_response(s, "{ 'execute': 'qmp_capabilities' }"); if (getenv("QTEST_STOP")) { kill(qtest_qemu_pid(s), SIGSTOP); @@ -291,16 +294,38 @@ redo: return words; } -void qtest_qmpv(QTestState *s, const char *fmt, va_list ap) +typedef struct { + JSONMessageParser parser; + QDict *response; +} QMPResponseParser; + +static void qmp_response(JSONMessageParser *parser, QList *tokens) +{ + QMPResponseParser *qmp = container_of(parser, QMPResponseParser, parser); + QObject *obj; + + obj = json_parser_parse(tokens, NULL); + if (!obj) { + fprintf(stderr, "QMP JSON response parsing failed\n"); + exit(1); + } + + g_assert(qobject_type(obj) == QTYPE_QDICT); + g_assert(!qmp->response); + qmp->response = (QDict *)obj; +} + +QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap) { - bool has_reply = false; - int nesting = 0; + QMPResponseParser qmp; /* Send QMP request */ socket_sendf(s->qmp_fd, fmt, ap); /* Receive reply */ - while (!has_reply || nesting > 0) { + qmp.response = NULL; + json_message_parser_init(&qmp.parser, qmp_response); + while (!qmp.response) { ssize_t len; char c; @@ -314,25 +339,39 @@ void qtest_qmpv(QTestState *s, const char *fmt, va_list ap) exit(1); } - switch (c) { - case '{': - nesting++; - has_reply = true; - break; - case '}': - nesting--; - break; - } + json_message_parser_feed(&qmp.parser, &c, 1); } + json_message_parser_destroy(&qmp.parser); + + return qmp.response; +} + +QDict *qtest_qmp(QTestState *s, const char *fmt, ...) +{ + va_list ap; + QDict *response; + + va_start(ap, fmt); + response = qtest_qmpv(s, fmt, ap); + va_end(ap); + return response; +} + +void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap) +{ + QDict *response = qtest_qmpv(s, fmt, ap); + QDECREF(response); } -void qtest_qmp(QTestState *s, const char *fmt, ...) +void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...) { va_list ap; + QDict *response; va_start(ap, fmt); - qtest_qmpv(s, fmt, ap); + response = qtest_qmpv(s, fmt, ap); va_end(ap); + QDECREF(response); } const char *qtest_get_arch(void) diff --git a/tests/libqtest.h b/tests/libqtest.h index a6e99bd023..9deebdcdfa 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -22,6 +22,7 @@ #include <stdbool.h> #include <stdarg.h> #include <sys/types.h> +#include "qapi/qmp/qdict.h" typedef struct QTestState QTestState; @@ -44,13 +45,32 @@ QTestState *qtest_init(const char *extra_args); void qtest_quit(QTestState *s); /** + * qtest_qmp_discard_response: + * @s: #QTestState instance to operate on. + * @fmt...: QMP message to send to qemu + * + * Sends a QMP message to QEMU and consumes the response. + */ +void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...); + +/** * qtest_qmp: * @s: #QTestState instance to operate on. * @fmt...: QMP message to send to qemu * - * Sends a QMP message to QEMU + * Sends a QMP message to QEMU and returns the response. + */ +QDict *qtest_qmp(QTestState *s, const char *fmt, ...); + +/** + * qtest_qmpv_discard_response: + * @s: #QTestState instance to operate on. + * @fmt: QMP message to send to QEMU + * @ap: QMP message arguments + * + * Sends a QMP message to QEMU and consumes the response. */ -void qtest_qmp(QTestState *s, const char *fmt, ...); +void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap); /** * qtest_qmpv: @@ -58,9 +78,9 @@ void qtest_qmp(QTestState *s, const char *fmt, ...); * @fmt: QMP message to send to QEMU * @ap: QMP message arguments * - * Sends a QMP message to QEMU. + * Sends a QMP message to QEMU and returns the response. */ -void qtest_qmpv(QTestState *s, const char *fmt, va_list ap); +QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap); /** * qtest_get_irq: @@ -334,14 +354,31 @@ static inline void qtest_end(void) * qmp: * @fmt...: QMP message to send to qemu * - * Sends a QMP message to QEMU + * Sends a QMP message to QEMU and returns the response. + */ +static inline QDict *qmp(const char *fmt, ...) +{ + va_list ap; + QDict *response; + + va_start(ap, fmt); + response = qtest_qmpv(global_qtest, fmt, ap); + va_end(ap); + return response; +} + +/** + * qmp_discard_response: + * @fmt...: QMP message to send to qemu + * + * Sends a QMP message to QEMU and consumes the response. */ -static inline void qmp(const char *fmt, ...) +static inline void qmp_discard_response(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - qtest_qmpv(global_qtest, fmt, ap); + qtest_qmpv_discard_response(global_qtest, fmt, ap); va_end(ap); } diff --git a/tests/m48t59-test.c b/tests/m48t59-test.c index 4081a5fdb2..6abc4c8bf0 100644 --- a/tests/m48t59-test.c +++ b/tests/m48t59-test.c @@ -249,7 +249,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); - s = qtest_start("-display none -rtc clock=vm"); + s = qtest_start("-rtc clock=vm"); qtest_add_func("/rtc/bcd/check-time", bcd_check_time); qtest_add_func("/rtc/fuzz-registers", fuzz_registers); diff --git a/tests/multiboot/Makefile b/tests/multiboot/Makefile new file mode 100644 index 0000000000..34cdd81a90 --- /dev/null +++ b/tests/multiboot/Makefile @@ -0,0 +1,18 @@ +CC=gcc +CCFLAGS=-m32 -Wall -Wextra -Werror -fno-stack-protector -nostdinc -fno-builtin +ASFLAGS=-m32 + +LD=ld +LDFLAGS=-melf_i386 -T link.ld +LIBS=$(shell $(CC) $(CCFLAGS) -print-libgcc-file-name) + +all: mmap.elf + +mmap.elf: start.o mmap.o libc.o + $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) + +%.o: %.c + $(CC) $(CCFLAGS) -c -o $@ $^ + +%.o: %.S + $(CC) $(ASFLAGS) -c -o $@ $^ diff --git a/tests/multiboot/libc.c b/tests/multiboot/libc.c new file mode 100644 index 0000000000..05abbd92cc --- /dev/null +++ b/tests/multiboot/libc.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "libc.h" + +static void print_char(char c) +{ + outb(0xe9, c); +} + +static void print_str(char *s) +{ + while (*s) { + print_char(*s++); + } +} + +static void print_num(uint64_t value, int base) +{ + char digits[] = "0123456789abcdef"; + char buf[32] = { 0 }; + int i = sizeof(buf) - 2; + + do { + buf[i--] = digits[value % base]; + value /= base; + } while (value); + + print_str(&buf[i + 1]); +} + +void printf(const char *fmt, ...) +{ + va_list ap; + uint64_t val; + char *str; + int base; + int has_long; + int alt_form; + + va_start(ap, fmt); + + for (; *fmt; fmt++) { + if (*fmt != '%') { + print_char(*fmt); + continue; + } + fmt++; + + if (*fmt == '#') { + fmt++; + alt_form = 1; + } else { + alt_form = 0; + } + + if (*fmt == 'l') { + fmt++; + if (*fmt == 'l') { + fmt++; + has_long = 2; + } else { + has_long = 1; + } + } else { + has_long = 0; + } + + switch (*fmt) { + case 'x': + case 'p': + base = 16; + goto convert_number; + case 'd': + case 'i': + case 'u': + base = 10; + goto convert_number; + case 'o': + base = 8; + goto convert_number; + + convert_number: + switch (has_long) { + case 0: + val = va_arg(ap, unsigned int); + break; + case 1: + val = va_arg(ap, unsigned long); + break; + case 2: + val = va_arg(ap, unsigned long long); + break; + } + + if (alt_form && base == 16) { + print_str("0x"); + } + + print_num(val, base); + break; + + case 's': + str = va_arg(ap, char*); + print_str(str); + break; + case '%': + print_char(*fmt); + break; + default: + print_char('%'); + print_char(*fmt); + break; + } + } + + va_end(ap); +} + + diff --git a/tests/multiboot/libc.h b/tests/multiboot/libc.h new file mode 100644 index 0000000000..80eec5b7a0 --- /dev/null +++ b/tests/multiboot/libc.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef LIBC_H +#define LIBC_H + +/* Integer types */ + +typedef unsigned long long uint64_t; +typedef unsigned int uint32_t; +typedef unsigned short uint16_t; +typedef unsigned char uint8_t; + +typedef signed long long int64_t; +typedef signed int int32_t; +typedef signed short int16_t; +typedef signed char int8_t; + +typedef uint32_t uintptr_t; + + +/* stdarg.h */ + +typedef __builtin_va_list va_list; +#define va_start(ap, X) __builtin_va_start(ap, X) +#define va_arg(ap, type) __builtin_va_arg(ap, type) +#define va_end(ap) __builtin_va_end(ap) + + +/* Port I/O functions */ + +static inline void outb(uint16_t port, uint8_t data) +{ + asm volatile ("outb %0, %1" : : "a" (data), "Nd" (port)); +} + + +/* Misc functions */ + +void printf(const char *fmt, ...); + +#endif diff --git a/tests/multiboot/link.ld b/tests/multiboot/link.ld new file mode 100644 index 0000000000..3d49b58c60 --- /dev/null +++ b/tests/multiboot/link.ld @@ -0,0 +1,19 @@ +ENTRY(_start) + +SECTIONS +{ + . = 0x100000; + .text : { + *(multiboot) + *(.text) + } + .data ALIGN(4096) : { + *(.data) + } + .rodata ALIGN(4096) : { + *(.rodata) + } + .bss ALIGN(4096) : { + *(.bss) + } +} diff --git a/tests/multiboot/mmap.c b/tests/multiboot/mmap.c new file mode 100644 index 0000000000..766b003f38 --- /dev/null +++ b/tests/multiboot/mmap.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "libc.h" +#include "multiboot.h" + +int test_main(uint32_t magic, struct mb_info *mbi) +{ + uintptr_t entry_addr; + struct mb_mmap_entry *entry; + + (void) magic; + + printf("Lower memory: %dk\n", mbi->mem_lower); + printf("Upper memory: %dk\n", mbi->mem_upper); + + printf("\ne820 memory map:\n"); + + for (entry_addr = mbi->mmap_addr; + entry_addr < mbi->mmap_addr + mbi->mmap_length; + entry_addr += entry->size + 4) + { + entry = (struct mb_mmap_entry*) entry_addr; + + printf("%#llx - %#llx: type %d [entry size: %d]\n", + entry->base_addr, + entry->base_addr + entry->length, + entry->type, + entry->size); + } + + printf("\nmmap start: %#x\n", mbi->mmap_addr); + printf("mmap end: %#x\n", mbi->mmap_addr + mbi->mmap_length); + printf("real mmap end: %#x\n", entry_addr); + + return 0; +} diff --git a/tests/multiboot/mmap.out b/tests/multiboot/mmap.out new file mode 100644 index 0000000000..e70b6eb45d --- /dev/null +++ b/tests/multiboot/mmap.out @@ -0,0 +1,93 @@ + + + +=== Running test case: mmap.elf === + +Lower memory: 639k +Upper memory: 130040k + +e820 memory map: +0x0 - 0x9fc00: type 1 [entry size: 20] +0x9fc00 - 0xa0000: type 2 [entry size: 20] +0xf0000 - 0x100000: type 2 [entry size: 20] +0x100000 - 0x7ffe000: type 1 [entry size: 20] +0x7ffe000 - 0x8000000: type 2 [entry size: 20] +0xfffc0000 - 0x100000000: type 2 [entry size: 20] + +mmap start: 0x9000 +mmap end: 0x9090 +real mmap end: 0x9090 + + +=== Running test case: mmap.elf -m 1.1M === + +Lower memory: 639k +Upper memory: 96k + +e820 memory map: +0x0 - 0x9fc00: type 1 [entry size: 20] +0x9fc00 - 0xa0000: type 2 [entry size: 20] +0xf0000 - 0x100000: type 2 [entry size: 20] +0x100000 - 0x118000: type 1 [entry size: 20] +0x118000 - 0x11a000: type 2 [entry size: 20] +0xfffc0000 - 0x100000000: type 2 [entry size: 20] + +mmap start: 0x9000 +mmap end: 0x9090 +real mmap end: 0x9090 + + +=== Running test case: mmap.elf -m 2G === + +Lower memory: 639k +Upper memory: 2096120k + +e820 memory map: +0x0 - 0x9fc00: type 1 [entry size: 20] +0x9fc00 - 0xa0000: type 2 [entry size: 20] +0xf0000 - 0x100000: type 2 [entry size: 20] +0x100000 - 0x7fffe000: type 1 [entry size: 20] +0x7fffe000 - 0x80000000: type 2 [entry size: 20] +0xfffc0000 - 0x100000000: type 2 [entry size: 20] + +mmap start: 0x9000 +mmap end: 0x9090 +real mmap end: 0x9090 + + +=== Running test case: mmap.elf -m 4G === + +Lower memory: 639k +Upper memory: 3668984k + +e820 memory map: +0x0 - 0x9fc00: type 1 [entry size: 20] +0x9fc00 - 0xa0000: type 2 [entry size: 20] +0xf0000 - 0x100000: type 2 [entry size: 20] +0x100000 - 0xdfffe000: type 1 [entry size: 20] +0xdfffe000 - 0xe0000000: type 2 [entry size: 20] +0xfffc0000 - 0x100000000: type 2 [entry size: 20] +0x100000000 - 0x120000000: type 1 [entry size: 20] + +mmap start: 0x9000 +mmap end: 0x90a8 +real mmap end: 0x90a8 + + +=== Running test case: mmap.elf -m 8G === + +Lower memory: 639k +Upper memory: 3668984k + +e820 memory map: +0x0 - 0x9fc00: type 1 [entry size: 20] +0x9fc00 - 0xa0000: type 2 [entry size: 20] +0xf0000 - 0x100000: type 2 [entry size: 20] +0x100000 - 0xdfffe000: type 1 [entry size: 20] +0xdfffe000 - 0xe0000000: type 2 [entry size: 20] +0xfffc0000 - 0x100000000: type 2 [entry size: 20] +0x100000000 - 0x220000000: type 1 [entry size: 20] + +mmap start: 0x9000 +mmap end: 0x90a8 +real mmap end: 0x90a8 diff --git a/tests/multiboot/multiboot.h b/tests/multiboot/multiboot.h new file mode 100644 index 0000000000..4eb1fbe5d4 --- /dev/null +++ b/tests/multiboot/multiboot.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MULTIBOOT_H +#define MULTIBOOT_H + +#include "libc.h" + +struct mb_info { + uint32_t flags; + uint32_t mem_lower; + uint32_t mem_upper; + uint32_t boot_device; + uint32_t cmdline; + uint32_t mods_count; + uint32_t mods_addr; + char syms[16]; + uint32_t mmap_length; + uint32_t mmap_addr; + uint32_t drives_length; + uint32_t drives_addr; + uint32_t config_table; + uint32_t boot_loader_name; + uint32_t apm_table; + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; +} __attribute__((packed)); + +struct mb_module { + uint32_t mod_start; + uint32_t mod_end; + uint32_t string; + uint32_t reserved; +} __attribute__((packed)); + +struct mb_mmap_entry { + uint32_t size; + uint64_t base_addr; + uint64_t length; + uint32_t type; +} __attribute__((packed)); + +#endif diff --git a/tests/multiboot/run_test.sh b/tests/multiboot/run_test.sh new file mode 100755 index 0000000000..97a9a49f8b --- /dev/null +++ b/tests/multiboot/run_test.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +# Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +QEMU=${QEMU:-"../../x86_64-softmmu/qemu-system-x86_64"} + +run_qemu() { + local kernel=$1 + shift + + echo -e "\n\n=== Running test case: $kernel $@ ===\n" >> test.log + + $QEMU \ + -kernel $kernel \ + -display none \ + -device isa-debugcon,chardev=stdio \ + -chardev file,path=test.out,id=stdio \ + -device isa-debug-exit,iobase=0xf4,iosize=0x4 \ + "$@" + ret=$? + + cat test.out >> test.log +} + +mmap() { + run_qemu mmap.elf + run_qemu mmap.elf -m 1.1M + run_qemu mmap.elf -m 2G + run_qemu mmap.elf -m 4G + run_qemu mmap.elf -m 8G +} + + +make all + +for t in mmap; do + + echo > test.log + $t + + debugexit=$((ret & 0x1)) + ret=$((ret >> 1)) + pass=1 + + if [ $debugexit != 1 ]; then + echo -e "\e[31m ?? \e[0m $t (no debugexit used, exit code $ret)" + pass=0 + elif [ $ret != 0 ]; then + echo -e "\e[31mFAIL\e[0m $t (exit code $ret)" + pass=0 + fi + + if ! diff $t.out test.log > /dev/null 2>&1; then + echo -e "\e[31mFAIL\e[0m $t (output difference)" + diff -u $t.out test.log + pass=0 + fi + + if [ $pass == 1 ]; then + echo -e "\e[32mPASS\e[0m $t" + fi + +done diff --git a/tests/multiboot/start.S b/tests/multiboot/start.S new file mode 100644 index 0000000000..7d33959650 --- /dev/null +++ b/tests/multiboot/start.S @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +.section multiboot + +#define MB_MAGIC 0x1badb002 +#define MB_FLAGS 0x0 +#define MB_CHECKSUM -(MB_MAGIC + MB_FLAGS) + +.align 4 +.int MB_MAGIC +.int MB_FLAGS +.int MB_CHECKSUM + +.section .text +.global _start +_start: + mov $stack, %esp + push %ebx + push %eax + call test_main + + /* Test device exit */ + outl %eax, $0xf4 + + cli + hlt + jmp . + +.section bss +.space 8192 +stack: diff --git a/tests/qdev-monitor-test.c b/tests/qdev-monitor-test.c new file mode 100644 index 0000000000..33a8ea4b9c --- /dev/null +++ b/tests/qdev-monitor-test.c @@ -0,0 +1,81 @@ +/* + * qdev-monitor.c test cases + * + * Copyright (C) 2013 Red Hat Inc. + * + * Authors: + * Stefan Hajnoczi <stefanha@redhat.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include <string.h> +#include <glib.h> +#include "libqtest.h" +#include "qapi/qmp/qjson.h" + +static void test_device_add(void) +{ + QDict *response; + QDict *error; + + qtest_start("-drive if=none,id=drive0"); + + /* Make device_add fail. If this leaks the virtio-blk-pci device then a + * reference to drive0 will also be held (via qdev properties). + */ + response = qmp("{\"execute\": \"device_add\"," + " \"arguments\": {" + " \"driver\": \"virtio-blk-pci\"," + " \"drive\": \"drive0\"" + "}}"); + g_assert(response); + error = qdict_get_qdict(response, "error"); + g_assert(!strcmp(qdict_get_try_str(error, "class") ?: "", + "GenericError")); + g_assert(!strcmp(qdict_get_try_str(error, "desc") ?: "", + "Device initialization failed.")); + QDECREF(response); + + /* Delete the drive */ + response = qmp("{\"execute\": \"human-monitor-command\"," + " \"arguments\": {" + " \"command-line\": \"drive_del drive0\"" + "}}"); + g_assert(response); + g_assert(!strcmp(qdict_get_try_str(response, "return") ?: "(null)", "")); + QDECREF(response); + + /* Try to re-add the drive. This fails with duplicate IDs if a leaked + * virtio-blk-pci exists that holds a reference to the old drive0. + */ + response = qmp("{\"execute\": \"human-monitor-command\"," + " \"arguments\": {" + " \"command-line\": \"drive_add pci-addr=auto if=none,id=drive0\"" + "}}"); + g_assert(response); + g_assert(!strcmp(qdict_get_try_str(response, "return") ?: "", + "OK\r\n")); + QDECREF(response); + + qtest_end(); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + /* Check architecture */ + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { + g_test_message("Skipping test for non-x86\n"); + return 0; + } + + /* Run the tests */ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/qmp/device_add", test_device_add); + + return g_test_run(); +} diff --git a/tests/qemu-iotests/017 b/tests/qemu-iotests/017 index 45f2c0b055..aba3faf712 100755 --- a/tests/qemu-iotests/017 +++ b/tests/qemu-iotests/017 @@ -66,7 +66,7 @@ echo "Creating test image with backing file" echo TEST_IMG=$TEST_IMG_SAVE -_make_test_img -b $TEST_IMG.base 6G +_make_test_img -b "$TEST_IMG.base" 6G echo "Filling test image" echo diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019 index cd3582cf6f..5bb18d0c0a 100755 --- a/tests/qemu-iotests/019 +++ b/tests/qemu-iotests/019 @@ -90,12 +90,12 @@ mv "$TEST_IMG" "$TEST_IMG.orig" # Test the conversion twice: One test with the old-style -B option and another # one with -o backing_file -for backing_option in "-B $TEST_IMG.base" "-o backing_file=$TEST_IMG.base"; do +for backing_option in "-B " "-o backing_file="; do echo - echo Testing conversion with $backing_option | _filter_testdir | _filter_imgfmt + echo Testing conversion with $backing_option$TEST_IMG.base | _filter_testdir | _filter_imgfmt echo - $QEMU_IMG convert -O $IMGFMT $backing_option "$TEST_IMG.orig" "$TEST_IMG" + $QEMU_IMG convert -O $IMGFMT $backing_option"$TEST_IMG.base" "$TEST_IMG.orig" "$TEST_IMG" echo "Checking if backing clusters are allocated when they shouldn't" echo diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index ae56f3b808..d0f96ea0e1 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -388,7 +388,9 @@ class TestStreamStop(iotests.QMPTestCase): def setUp(self): qemu_img('create', backing_img, str(TestStreamStop.image_len)) + qemu_io('-c', 'write -P 0x1 0 32M', backing_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) + qemu_io('-c', 'write -P 0x1 32M 32M', test_img) self.vm = iotests.VM().add_drive(test_img) self.vm.launch() @@ -414,7 +416,9 @@ class TestSetSpeed(iotests.QMPTestCase): def setUp(self): qemu_img('create', backing_img, str(TestSetSpeed.image_len)) + qemu_io('-c', 'write -P 0x1 0 32M', backing_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) + qemu_io('-c', 'write -P 0x1 32M 32M', test_img) self.vm = iotests.VM().add_drive(test_img) self.vm.launch() diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index f85b4ce63f..8bade92a80 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -54,7 +54,7 @@ echo "== Checking that image is clean on shutdown ==" IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img $size -$QEMU_IO -c "write -P 0x5a 0 512" ""$TEST_IMG"" | _filter_qemu_io +$QEMU_IO -c "write -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must not be set ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index aad535a74b..a2e18c56d4 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -54,22 +54,12 @@ class ImageCommitTestCase(iotests.QMPTestCase): self.assert_no_active_commit() - def create_image(self, name, size): - file = open(name, 'w') - i = 0 - while i < size: - sector = struct.pack('>l504xl', i / 512, i / 512) - file.write(sector) - i = i + 512 - file.close() - - class TestSingleDrive(ImageCommitTestCase): image_len = 1 * 1024 * 1024 test_len = 1 * 1024 * 256 def setUp(self): - self.create_image(backing_img, TestSingleDrive.image_len) + iotests.create_image(backing_img, TestSingleDrive.image_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, mid_img) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % mid_img, test_img) qemu_io('-c', 'write -P 0xab 0 524288', backing_img) @@ -167,7 +157,7 @@ class TestRelativePaths(ImageCommitTestCase): except OSError as exception: if exception.errno != errno.EEXIST: raise - self.create_image(self.backing_img_abs, TestRelativePaths.image_len) + iotests.create_image(self.backing_img_abs, TestRelativePaths.image_len) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.backing_img_abs, self.mid_img_abs) qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % self.mid_img_abs, self.test_img) qemu_img('rebase', '-u', '-b', self.backing_img, self.mid_img_abs) diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 6661c0395d..5d40265fb3 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -677,5 +677,30 @@ class TestSetSpeed(ImageMirroringTestCase): self.wait_ready_and_cancel() +class TestUnbackedSource(ImageMirroringTestCase): + image_len = 2 * 1024 * 1024 # MB + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, test_img, + str(TestUnbackedSource.image_len)) + self.vm = iotests.VM().add_drive(test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + os.remove(target_img) + + def test_absolute_paths(self): + self.assert_no_active_block_jobs() + + for sync_mode in ['full', 'top', 'none']: + result = self.vm.qmp('drive-mirror', device='drive0', + sync=sync_mode, target=target_img, + mode='absolute-paths') + self.assert_qmp(result, 'return', {}) + self.complete_and_wait() + self.assert_no_active_block_jobs() + if __name__ == '__main__': iotests.main(supported_fmts=['qcow2', 'qed']) diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out index 42314e9c00..4fd1c2dcd2 100644 --- a/tests/qemu-iotests/041.out +++ b/tests/qemu-iotests/041.out @@ -1,5 +1,5 @@ -........................ +......................... ---------------------------------------------------------------------- -Ran 24 tests +Ran 25 tests OK diff --git a/tests/qemu-iotests/048 b/tests/qemu-iotests/048 index 9b9d118ef3..9def7fcc8c 100755 --- a/tests/qemu-iotests/048 +++ b/tests/qemu-iotests/048 @@ -74,5 +74,39 @@ _compare io_pattern write 0 $CLUSTER_SIZE 0 1 123 _compare +# Test unaligned case of mismatch offsets in allocated clusters +_make_test_img $size +io_pattern write 0 512 0 1 100 +cp "$TEST_IMG" "$TEST_IMG2" +io_pattern write 512 512 0 1 101 +_compare + +# Test cluster allocated in one, with IO error +cat > "$TEST_DIR/blkdebug.conf"<<EOF +[inject-error] +event = "read_aio" +errno = "5" +once ="off" +EOF +_make_test_img $size +cp "$TEST_IMG" "$TEST_IMG2" +io_pattern write 512 512 0 1 102 +TEST_IMG="blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG" _compare 2>&1 |\ + _filter_testdir | _filter_imgfmt + +# Test cluster allocated in one, with different sizes and IO error in the part +# that exists only in one image +cat > "$TEST_DIR/blkdebug.conf"<<EOF +[inject-error] +event = "read_aio" +errno = "5" +once ="off" +EOF +_make_test_img $size +TEST_IMG="$TEST_IMG2" _make_test_img 0 +io_pattern write 512 512 0 1 102 +TEST_IMG="blkdebug:$TEST_DIR/blkdebug.conf:$TEST_IMG" _compare 2>&1 |\ + _filter_testdir | _filter_imgfmt + # Cleanup status=0 diff --git a/tests/qemu-iotests/048.out b/tests/qemu-iotests/048.out index 68f65d5e19..d141e0579f 100644 --- a/tests/qemu-iotests/048.out +++ b/tests/qemu-iotests/048.out @@ -1,5 +1,5 @@ QA output created by 048 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 === IO: pattern 45 qemu-io> wrote 4096/4096 bytes at offset 524288 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -28,4 +28,29 @@ qemu-io> wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-io> Content mismatch at offset 0! 1 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +=== IO: pattern 100 +qemu-io> wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> === IO: pattern 101 +qemu-io> wrote 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> Content mismatch at offset 512! +1 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +=== IO: pattern 102 +qemu-io> wrote 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> qemu-img: Error while reading offset 0 of blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +qemu-img: Error while reading offset 0: Input/output error +4 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/t.IMGFMT.2', fmt=IMGFMT size=0 +=== IO: pattern 102 +qemu-io> wrote 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-io> qemu-img: Error while reading offset 0 of blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +qemu-img: Error while reading offset 0 of blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +Warning: Image size mismatch! +4 Cleanup diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 356c3756f4..3a75bda5eb 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -64,9 +64,9 @@ function run_qemu() size=128M _make_test_img $size -cp $TEST_IMG $TEST_IMG.orig -mv $TEST_IMG $TEST_IMG.base -_make_test_img -b $TEST_IMG.base $size +cp "$TEST_IMG" "$TEST_IMG.orig" +mv "$TEST_IMG" "$TEST_IMG.base" +_make_test_img -b "$TEST_IMG.base" $size echo echo === Unknown option === @@ -78,10 +78,17 @@ run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=1234 run_qemu -drive file="$TEST_IMG",format=qcow2,unknown_opt=foo echo +echo === Invalid format === +echo + +run_qemu -drive file="$TEST_IMG",format=foo +run_qemu -drive file="$TEST_IMG",driver=foo + +echo echo === Overriding backing file === echo -echo "info block" | run_qemu -drive file=$TEST_IMG,driver=qcow2,backing.file.filename=$TEST_IMG.orig -nodefaults +echo "info block" | run_qemu -drive file="$TEST_IMG",driver=qcow2,backing.file.filename="$TEST_IMG.orig" -nodefaults echo echo === Enable and disable lazy refcounting on the command line, plus some invalid values === diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 2839e32807..8769c8e66e 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -17,6 +17,15 @@ Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' +=== Invalid format === + +Testing: -drive file=TEST_DIR/t.qcow2,format=foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=foo: 'foo' invalid format + +Testing: -drive file=TEST_DIR/t.qcow2,driver=foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=foo: could not open disk image TEST_DIR/t.qcow2: Invalid driver: 'foo' + + === Overriding backing file === Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,backing.file.filename=TEST_DIR/t.qcow2.orig -nodefaults @@ -24,7 +33,7 @@ QEMU X.Y.Z monitor - type 'help' for more information (qemu) i[K[Din[K[D[Dinf[K[D[D[Dinfo[K[D[D[D[Dinfo [K[D[D[D[D[Dinfo b[K[D[D[D[D[D[Dinfo bl[K[D[D[D[D[D[D[Dinfo blo[K[D[D[D[D[D[D[D[Dinfo bloc[K[D[D[D[D[D[D[D[D[Dinfo block[K ide0-hd0: TEST_DIR/t.qcow2 (qcow2) Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1) - [not inserted](qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K === Enable and disable lazy refcounting on the command line, plus some invalid values === @@ -226,6 +235,6 @@ Testing: -drive file=foo:bar QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: Unknown protocol Testing: -drive file.filename=foo:bar -QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open file: No such file or directory +QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open 'foo:bar': No such file or directory *** done diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index b81c575d94..6a27ac978b 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -69,7 +69,7 @@ poke_file "$TEST_IMG" "$grain_table_size_offset" "\x01\x00\x00\x00" echo echo "=== Testing monolithicFlat creation and opening ===" IMGOPTS="subformat=monolithicFlat" _make_test_img 2G -$QEMU_IMG info $TEST_IMG | _filter_testdir +_img_info echo echo "=== Testing monolithicFlat with zeroed_grain ===" diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out index 9b12efb466..2ded8a950a 100644 --- a/tests/qemu-iotests/059.out +++ b/tests/qemu-iotests/059.out @@ -18,10 +18,9 @@ no file open, try 'help open' === Testing monolithicFlat creation and opening === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2147483648 -image: TEST_DIR/t.vmdk -file format: vmdk +image: TEST_DIR/t.IMGFMT +file format: IMGFMT virtual size: 2.0G (2147483648 bytes) -disk size: 4.0K === Testing monolithicFlat with zeroed_grain === qemu-img: TEST_DIR/t.IMGFMT: Flat image can't enable zeroed grain diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index fa9319da26..e42f9bd5e8 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -163,7 +163,7 @@ echo "=== Testing zero expansion on backed image ===" echo IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io -IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M +IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 64M $QEMU_IO -c "read -P 0x2a 0 128k" -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" _check_test_img @@ -174,7 +174,7 @@ echo "=== Testing zero expansion on backed inactive clusters ===" echo IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io -IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M +IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 64M $QEMU_IO -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG snapshot -c foo "$TEST_IMG" $QEMU_IO -c "write -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io @@ -190,7 +190,7 @@ echo "=== Testing zero expansion on backed image with shared L2 table ===" echo IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M $QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io -IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M +IMGOPTS="compat=1.1" _make_test_img -b "$TEST_IMG.base" 64M $QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG snapshot -c foo "$TEST_IMG" $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" diff --git a/tests/qemu-iotests/064 b/tests/qemu-iotests/064 index 6789aa6ee4..1c74c31a1a 100755 --- a/tests/qemu-iotests/064 +++ b/tests/qemu-iotests/064 @@ -56,6 +56,17 @@ echo echo "=== Verify pattern 0x00, 66M - 1024M ===" $QEMU_IO -r -c "read -pP 0x00 66M 958M" "$TEST_IMG" | _filter_qemu_io +echo +echo "=== Verify pattern write, 0xc3 99M-157M ===" +$QEMU_IO -c "write -pP 0xc3 99M 58M" "$TEST_IMG" | _filter_qemu_io +# first verify we didn't write where we should not have +$QEMU_IO -c "read -pP 0xa5 0 33M" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -pP 0x96 33M 33M" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -pP 0x00 66M 33M" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "read -pP 0x00 157MM 867MM" "$TEST_IMG" | _filter_qemu_io +# now verify what we should have actually written +$QEMU_IO -c "read -pP 0xc3 99M 58M" "$TEST_IMG" | _filter_qemu_io + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/064.out b/tests/qemu-iotests/064.out index b9e8e4a873..5346a4e630 100644 --- a/tests/qemu-iotests/064.out +++ b/tests/qemu-iotests/064.out @@ -11,4 +11,18 @@ read 34603008/34603008 bytes at offset 34603008 === Verify pattern 0x00, 66M - 1024M === read 1004535808/1004535808 bytes at offset 69206016 958 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Verify pattern write, 0xc3 99M-157M === +wrote 60817408/60817408 bytes at offset 103809024 +58 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 34603008/34603008 bytes at offset 0 +33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 34603008/34603008 bytes at offset 34603008 +33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 34603008/34603008 bytes at offset 69206016 +33 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 909115392/909115392 bytes at offset 164626432 +867 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 60817408/60817408 bytes at offset 103809024 +58 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067 index 79dc38bc04..d025192c83 100755 --- a/tests/qemu-iotests/067 +++ b/tests/qemu-iotests/067 @@ -45,7 +45,7 @@ function do_run_qemu() function run_qemu() { - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | sed -e 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' } size=128M diff --git a/tests/qemu-iotests/067.out b/tests/qemu-iotests/067.out index 4bb9ff9652..8d271cc41a 100644 --- a/tests/qemu-iotests/067.out +++ b/tests/qemu-iotests/067.out @@ -6,7 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk -device virtio-blk-pci,drive=disk,id=virtio0 QMP_VERSION {"return": {}} -{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} +{"return": [{"io-status": "ok", "device": "disk", "locked": false, "removable": false, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} @@ -24,7 +24,7 @@ QMP_VERSION Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,if=none,id=disk QMP_VERSION {"return": {}} -{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} +{"return": [{"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}]} {"return": {}} {"return": {}} {"return": {}} @@ -44,7 +44,7 @@ Testing: QMP_VERSION {"return": {}} {"return": "OK\r\n"} -{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} +{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} {"return": {}} {"return": {}} {"return": {}} @@ -64,14 +64,14 @@ Testing: QMP_VERSION {"return": {}} {"return": {}} -{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} +{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} {"return": {}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"path": "/machine/peripheral/virtio0/virtio-backend"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_DELETED", "data": {"device": "virtio0", "path": "/machine/peripheral/virtio0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "RESET"} -{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": 139264, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} +{"return": [{"io-status": "ok", "device": "ide1-cd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "floppy0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"device": "sd0", "locked": false, "removable": true, "tray_open": false, "type": "unknown"}, {"io-status": "ok", "device": "disk", "locked": false, "removable": true, "inserted": {"iops_rd": 0, "image": {"virtual-size": 134217728, "filename": "TEST_DIR/t.qcow2", "cluster-size": 65536, "format": "qcow2", "actual-size": SIZE, "format-specific": {"type": "qcow2", "data": {"compat": "1.1", "lazy-refcounts": false}}, "dirty-flag": false}, "iops_wr": 0, "ro": false, "backing_file_depth": 0, "drv": "qcow2", "iops": 0, "bps_wr": 0, "encrypted": false, "bps": 0, "bps_rd": 0, "file": "TEST_DIR/t.qcow2", "encryption_key_missing": false}, "tray_open": false, "type": "unknown"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "ide1-cd0", "tray-open": true}} diff --git a/tests/qemu-iotests/068 b/tests/qemu-iotests/068 new file mode 100755 index 0000000000..b72e55599b --- /dev/null +++ b/tests/qemu-iotests/068 @@ -0,0 +1,65 @@ +#!/bin/bash +# +# Test case for loading a saved VM state from a qcow2 image +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# This tests qocw2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + +IMGOPTS="compat=1.1" +IMG_SIZE=128K + +echo +echo "=== Saving and reloading a VM state to/from a qcow2 image ===" +echo +_make_test_img $IMG_SIZE +# Give qemu some time to boot before saving the VM state +bash -c 'sleep 1; echo -e "savevm 0\nquit"' |\ + $QEMU -nographic -monitor stdio -serial none -hda "$TEST_IMG" |\ + _filter_qemu +# Now try to continue from that VM state (this should just work) +echo quit |\ + $QEMU -nographic -monitor stdio -serial none -hda "$TEST_IMG" -loadvm 0 |\ + _filter_qemu + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/068.out b/tests/qemu-iotests/068.out new file mode 100644 index 0000000000..abe35a9f8c --- /dev/null +++ b/tests/qemu-iotests/068.out @@ -0,0 +1,11 @@ +QA output created by 068 + +=== Saving and reloading a VM state to/from a qcow2 image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) s[K[Dsa[K[D[Dsav[K[D[D[Dsave[K[D[D[D[Dsavev[K[D[D[D[D[Dsavevm[K[D[D[D[D[D[Dsavevm [K[D[D[D[D[D[D[Dsavevm 0[K +(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K +*** done diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069 new file mode 100755 index 0000000000..3042803a81 --- /dev/null +++ b/tests/qemu-iotests/069 @@ -0,0 +1,59 @@ +#!/bin/bash +# +# Test case for deleting a backing file +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt cow qed qcow qcow2 vmdk +_supported_proto generic +_supported_os Linux + +IMG_SIZE=128K + +echo +echo "=== Creating an image with a backing file and deleting that file ===" +echo +TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE +_make_test_img -b "$TEST_IMG.base" $IMG_SIZE +rm -f "$TEST_IMG.base" +# Just open the image and close it right again (this should print an error message) +$QEMU_IO -c quit "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/069.out b/tests/qemu-iotests/069.out new file mode 100644 index 0000000000..b48306d5ab --- /dev/null +++ b/tests/qemu-iotests/069.out @@ -0,0 +1,8 @@ +QA output created by 069 + +=== Creating an image with a backing file and deleting that file === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=131072 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 backing_file='TEST_DIR/t.IMGFMT.base' +qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open backing file: Could not open 'TEST_DIR/t.IMGFMT.base': No such file or directory +*** done diff --git a/tests/qemu-iotests/070 b/tests/qemu-iotests/070 new file mode 100755 index 0000000000..41bf100701 --- /dev/null +++ b/tests/qemu-iotests/070 @@ -0,0 +1,67 @@ +#!/bin/bash +# +# Test VHDX log replay from an image with a journal that needs to be +# replayed +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=jcody@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt vhdx +_supported_proto generic +_supported_os Linux + +# With the log replayed, the pattern 0xa5 extends to 0xc025000 +# If the log was not replayed, it would only extend to 0xc000000 +# +# This image is a 10G dynamic image, with 4M block size, and 1 unplayed +# data sector in the log +# +# This image was created with qemu-img, however it was verified using +# Hyper-V to properly replay the logs and give the same post-replay +# image as qemu. +_use_sample_img iotest-dirtylog-10G-4M.vhdx.bz2 + +echo +echo "=== Verify open image read-only fails, due to dirty log ===" +$QEMU_IO -r -c "read -pP 0xa5 0 18M" "$TEST_IMG" 2>&1 | grep -o "Permission denied" + +echo "=== Verify open image replays log ===" +$QEMU_IO -c "read -pP 0xa5 0 18M" "$TEST_IMG" | _filter_qemu_io + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/070.out b/tests/qemu-iotests/070.out new file mode 100644 index 0000000000..9db8ff2650 --- /dev/null +++ b/tests/qemu-iotests/070.out @@ -0,0 +1,8 @@ +QA output created by 070 + +=== Verify open image read-only fails, due to dirty log === +Permission denied +=== Verify open image replays log === +read 18874368/18874368 bytes at offset 0 +18 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/073 b/tests/qemu-iotests/073 new file mode 100755 index 0000000000..392db54999 --- /dev/null +++ b/tests/qemu-iotests/073 @@ -0,0 +1,166 @@ +#!/bin/bash +# +# Test count_contiguous_clusters in qcow2 +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + +CLUSTER_SIZE=64k +size=128M + +echo +echo "== creating backing file ==" + +TEST_IMG="$TEST_IMG.base" _make_test_img $size + +_make_test_img -b "$TEST_IMG.base" +$QEMU_IO -c "write -P 0xa5 0 $size" "$TEST_IMG.base" | _filter_qemu_io + +echo +echo "== normal -> unallocated ==" + +$QEMU_IO -c "write -P 0x11 0 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x11 0x10000 0x10000" "$TEST_IMG.base" | _filter_qemu_io + +$QEMU_IO -c "read -P 0x11 0 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo "== normal -> compressed ==" + +$QEMU_IO -c "write -P 0x22 0x20000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -c -P 0x22 0x30000 0x10000" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "read -P 0x22 0x20000 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo "== normal -> zero ==" + +$QEMU_IO -c "write -P 0x33 0x40000 0x20000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x33 0x40000 0x20000" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -P 0 0x40000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 0x50000 0x10000" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "read -P 0 0x40000 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo +echo "== unallocated -> normal ==" + +$QEMU_IO -c "write -P 0x44 0x60000 0x10000" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -P 0x44 0x70000 0x10000" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "read -P 0x44 0x60000 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo "== unallocated -> compressed ==" + +$QEMU_IO -c "write -P 0x55 0x80000 0x10000" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -c -P 0x55 0x90000 0x10000" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "read -P 0x55 0x80000 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo "== unallocated -> zero ==" + +$QEMU_IO -c "write -P 0x66 0xa0000 0x20000" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -P 0 0xa0000 0x10000" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "write -z 0xb0000 0x10000" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "read -P 0 0xa0000 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo +echo "== compressed -> normal ==" + +$QEMU_IO -c "write -c -P 0x77 0xc0000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x77 0xd0000 0x10000" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "read -P 0x77 0xc0000 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo "== compressed -> unallocated ==" + +$QEMU_IO -c "write -c -P 0x88 0xe0000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0x88 0xf0000 0x10000" "$TEST_IMG.base" | _filter_qemu_io + +$QEMU_IO -c "read -P 0x88 0xe0000 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo "== compressed -> zero ==" + +$QEMU_IO -c "write -c -P 0 0x100000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -c -P 0x99 0x110000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 0x110000 0x10000" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "read -P 0 0x100000 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo +echo "== zero -> normal ==" + +$QEMU_IO -c "write -P 0xaa 0x120000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0 0x130000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 0x120000 0x10000" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "read -P 0 0x120000 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo "== zero -> unallocated ==" + +$QEMU_IO -c "write -z 0x140000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -P 0 0x150000 0x10000" "$TEST_IMG.base" | _filter_qemu_io + +$QEMU_IO -c "read -P 0 0x140000 0x20000" "$TEST_IMG" | _filter_qemu_io + +echo +echo "== zero -> compressed ==" + +$QEMU_IO -c "write -c -P 0 0x170000 0x10000" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write -z 0x160000 0x10000" "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO -c "read -P 0 0x160000 0x20000" "$TEST_IMG" | _filter_qemu_io + + +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/073.out b/tests/qemu-iotests/073.out new file mode 100644 index 0000000000..c9b00763b2 --- /dev/null +++ b/tests/qemu-iotests/073.out @@ -0,0 +1,118 @@ +QA output created by 073 + +== creating backing file == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' +wrote 134217728/134217728 bytes at offset 0 +128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== normal -> unallocated == +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== normal -> compressed == +wrote 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 196608 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 131072 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== normal -> zero == +wrote 131072/131072 bytes at offset 262144 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 262144 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 262144 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 327680 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 262144 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + +== unallocated -> normal == +wrote 65536/65536 bytes at offset 393216 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 458752 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 393216 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== unallocated -> compressed == +wrote 65536/65536 bytes at offset 524288 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 589824 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 524288 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== unallocated -> zero == +wrote 131072/131072 bytes at offset 655360 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 655360 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 720896 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 655360 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + +== compressed -> normal == +wrote 65536/65536 bytes at offset 786432 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 851968 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 786432 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== compressed -> unallocated == +wrote 65536/65536 bytes at offset 917504 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 983040 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 917504 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== compressed -> zero == +wrote 65536/65536 bytes at offset 1048576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 1114112 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 1114112 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1048576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + +== zero -> normal == +wrote 65536/65536 bytes at offset 1179648 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 1245184 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 1179648 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1179648 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== zero -> unallocated == +wrote 65536/65536 bytes at offset 1310720 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 1376256 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1310720 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== zero -> compressed == +wrote 65536/65536 bytes at offset 1507328 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 1441792 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1441792 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index 2932e14e73..8cde7f11fa 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -200,7 +200,6 @@ testlist options -vhdx) IMGFMT=vhdx xpand=false - IMGFMT_GENERIC=false ;; -rbd) diff --git a/tests/qemu-iotests/common.pattern b/tests/qemu-iotests/common.pattern index 00e0f605fd..ddfbca1b76 100644 --- a/tests/qemu-iotests/common.pattern +++ b/tests/qemu-iotests/common.pattern @@ -28,7 +28,7 @@ function do_is_allocated() { } function is_allocated() { - do_is_allocated "$@" | $QEMU_IO $TEST_IMG | _filter_qemu_io + do_is_allocated "$@" | $QEMU_IO "$TEST_IMG" | _filter_qemu_io } function do_io() { @@ -46,18 +46,18 @@ function do_io() { } function io_pattern() { - do_io "$@" | $QEMU_IO $TEST_IMG | _filter_qemu_io + do_io "$@" | $QEMU_IO "$TEST_IMG" | _filter_qemu_io } function io() { local start=$2 local pattern=$(( (start >> 9) % 256 )) - do_io "$@" $pattern | $QEMU_IO $TEST_IMG | _filter_qemu_io + do_io "$@" $pattern | $QEMU_IO "$TEST_IMG" | _filter_qemu_io } function io_zero() { - do_io "$@" 0 | $QEMU_IO $TEST_IMG | _filter_qemu_io + do_io "$@" 0 | $QEMU_IO "$TEST_IMG" | _filter_qemu_io } function io_test() { @@ -117,8 +117,8 @@ function io_test2() { echo === Clusters to be compressed [3] io_pattern writev $((offset + 8 * $cluster_size)) $cluster_size $((9 * $cluster_size)) $num 165 - mv $TEST_IMG $TEST_IMG.orig - $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c $TEST_IMG.orig $TEST_IMG + mv "$TEST_IMG" "$TEST_IMG.orig" + $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c "$TEST_IMG.orig" "$TEST_IMG" # Write the used clusters echo === Used clusters [1] diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 4e826040d4..7f6245770a 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -111,6 +111,8 @@ _make_test_img() local image_size=$* local optstr="" local img_name="" + local use_backing=0 + local backing_file="" if [ -n "$TEST_IMG_FILE" ]; then img_name=$TEST_IMG_FILE @@ -123,7 +125,8 @@ _make_test_img() fi if [ "$1" = "-b" ]; then - extra_img_options="$1 $2" + use_backing=1 + backing_file=$2 image_size=$3 fi if [ \( "$IMGFMT" = "qcow2" -o "$IMGFMT" = "qed" \) -a -n "$CLUSTER_SIZE" ]; then @@ -135,7 +138,13 @@ _make_test_img() fi # XXX(hch): have global image options? - $QEMU_IMG create -f $IMGFMT $extra_img_options $img_name $image_size 2>&1 | \ + ( + if [ $use_backing = 1 ]; then + $QEMU_IMG create -f $IMGFMT $extra_img_options -b "$backing_file" "$img_name" $image_size 2>&1 + else + $QEMU_IMG create -f $IMGFMT $extra_img_options "$img_name" $image_size 2>&1 + fi + ) | \ sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ @@ -148,7 +157,10 @@ _make_test_img() -e "s# zeroed_grain=\\(on\\|off\\)##g" \ -e "s# subformat='[^']*'##g" \ -e "s# adapter_type='[^']*'##g" \ - -e "s# lazy_refcounts=\\(on\\|off\\)##g" + -e "s# lazy_refcounts=\\(on\\|off\\)##g" \ + -e "s# block_size=[0-9]\\+##g" \ + -e "s# block_state_zero=\\(on\\|off\\)##g" \ + -e "s# log_size=[0-9]\\+##g" # Start an NBD server on the image file, which is what we'll be talking to if [ $IMGPROTO = "nbd" ]; then diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 13c5500f54..b63b18c7aa 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -73,3 +73,7 @@ 065 rw auto 066 rw auto 067 rw auto +068 rw auto +069 rw auto +070 rw auto +073 rw auto diff --git a/tests/qemu-iotests/sample_images/iotest-dirtylog-10G-4M.vhdx.bz2 b/tests/qemu-iotests/sample_images/iotest-dirtylog-10G-4M.vhdx.bz2 new file mode 100644 index 0000000000..4b91cfc654 --- /dev/null +++ b/tests/qemu-iotests/sample_images/iotest-dirtylog-10G-4M.vhdx.bz2 Binary files differdiff --git a/tests/qom-test.c b/tests/qom-test.c new file mode 100644 index 0000000000..499be40261 --- /dev/null +++ b/tests/qom-test.c @@ -0,0 +1,253 @@ +/* + * QTest testcase for QOM + * + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * 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 "libqtest.h" + +#include <glib.h> +#include <string.h> +#include "qemu/osdep.h" + +static void test_nop(gconstpointer data) +{ + QTestState *s; + const char *machine = data; + char *args; + + args = g_strdup_printf("-machine %s", machine); + s = qtest_start(args); + if (s) { + qtest_quit(s); + } + g_free(args); +} + +static const char *x86_machines[] = { + "pc", + "isapc", + "q35", +}; + +static const char *alpha_machines[] = { + "clipper", +}; + +static const char *arm_machines[] = { + "integratorcp", + "versatilepb", + "versatileab", + "lm3s811evb", + "lm3s6965evb", + "collie", + "akita", + "spitz", + "borzoi", + "terrier", + "tosa", + "cheetah", + "sx1-v1", + "sx1", + "realview-eb", + "realview-eb-mpcore", + "realview-pb-a8", + "realview-pbx-a9", + "musicpal", + "mainstone", + "connex", + "verdex", + "z2", + "n800", + "n810", + "kzm", + "vexpress-a9", + "vexpress-a15", + "smdkc210", + "nuri", + "xilinx-zynq-a9", + "highbank", + "midway", +}; + +static const char *cris_machines[] = { + "axis-dev88", +}; + +static const char *lm32_machines[] = { + "lm32-evr", + "lm32-uclinux", + "milkymist", +}; + +static const char *m68k_machines[] = { + "mcf5208evb", + "an5206", + "dummy", +}; + +static const char *microblaze_machines[] = { + "petalogix-ml605", + "petalogix-s3adsp1800", +}; + +static const char *mips_machines[] = { + "malta", + "magnum", + "mips", + "mipssim", + "pica61", +}; + +static const char *moxie_machines[] = { + "moxiesim", +}; + +static const char *openrisc_machines[] = { + "or32-sim", +}; + +static const char *ppc_machines[] = { + "g3beige", + "mac99", + "prep", + "mpc8544ds", + "ppce500", +}; + +static const char *ppc64_machines[] = { + "pseries", +}; + +static const char *ppc405_machines[] = { + "ref405ep", + "taihu", +}; + +static const char *ppc440_machines[] = { + "bamboo", + "virtex-ml507", +}; + +static const char *s390_machines[] = { + "s390-virtio", + "s390-ccw-virtio", +}; + +static const char *superh_machines[] = { + "r2d", + "shix", +}; + +static const char *sparc_machines[] = { + "SS-4", + "SS-5", + "SS-10", + "SS-20", + "SS-600MP", + "LX", + "SPARCClassic", + "SPARCbook", + "leon3_generic", +}; + +static const char *sparc64_machines[] = { + "sun4u", + "sun4v", + "Niagara", +}; + +static const char *unicore32_machines[] = { + "puv3", +}; + +static const char *xtensa_machines[] = { + "sim", + "lx60", + "lx200", +}; + +static void add_test_cases(const char *arch, const char *machine) +{ + char *path; + path = g_strdup_printf("/%s/qom/%s", arch, machine); + g_test_add_data_func(path, machine, test_nop); +} + +#define ADD_MACHINE_TESTS(arch, array) do { \ + int i; \ + for (i = 0; i < ARRAY_SIZE(array); i++) { \ + add_test_cases((arch), (array)[i]); \ + } \ +} while (false) + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + add_test_cases(arch, "none"); + + if (strcmp(arch, "i386") == 0 || + strcmp(arch, "x86_64") == 0) { + ADD_MACHINE_TESTS(arch, x86_machines); + } else if (strcmp(arch, "alpha") == 0) { + ADD_MACHINE_TESTS(arch, alpha_machines); + } else if (strcmp(arch, "arm") == 0) { + ADD_MACHINE_TESTS(arch, arm_machines); + } else if (strcmp(arch, "cris") == 0) { + ADD_MACHINE_TESTS(arch, cris_machines); + } else if (strcmp(arch, "lm32") == 0) { + ADD_MACHINE_TESTS(arch, lm32_machines); + } else if (strcmp(arch, "m68k") == 0) { + ADD_MACHINE_TESTS(arch, m68k_machines); + } else if (strcmp(arch, "microblaze") == 0 || + strcmp(arch, "microblazeel") == 0) { + ADD_MACHINE_TESTS(arch, microblaze_machines); + } else if (strcmp(arch, "mips") == 0 || + strcmp(arch, "mipsel") == 0 || + strcmp(arch, "mips64") == 0) { + ADD_MACHINE_TESTS(arch, mips_machines); + } else if (strcmp(arch, "mips64el") == 0) { + ADD_MACHINE_TESTS(arch, mips_machines); + add_test_cases(arch, "fulong2e"); + } else if (strcmp(arch, "moxie") == 0) { + ADD_MACHINE_TESTS(arch, moxie_machines); + } else if (strcmp(arch, "or32") == 0) { + ADD_MACHINE_TESTS(arch, openrisc_machines); + } else if (strcmp(arch, "ppcemb") == 0) { +#if 0 + /* XXX Available in ppcemb but don't work */ + ADD_MACHINE_TESTS(arch, ppc405_machines); +#endif + ADD_MACHINE_TESTS(arch, ppc440_machines); + } else if (strcmp(arch, "ppc") == 0) { + ADD_MACHINE_TESTS(arch, ppc405_machines); + ADD_MACHINE_TESTS(arch, ppc440_machines); + ADD_MACHINE_TESTS(arch, ppc_machines); + } else if (strcmp(arch, "ppc64") == 0) { + ADD_MACHINE_TESTS(arch, ppc405_machines); + ADD_MACHINE_TESTS(arch, ppc440_machines); + ADD_MACHINE_TESTS(arch, ppc_machines); + ADD_MACHINE_TESTS(arch, ppc64_machines); + } else if (strcmp(arch, "s390x") == 0) { + ADD_MACHINE_TESTS(arch, s390_machines); + } else if (strcmp(arch, "sh4") == 0 || + strcmp(arch, "sh4eb") == 0) { + ADD_MACHINE_TESTS(arch, superh_machines); + } else if (strcmp(arch, "sparc") == 0) { + ADD_MACHINE_TESTS(arch, sparc_machines); + } else if (strcmp(arch, "sparc64") == 0) { + ADD_MACHINE_TESTS(arch, sparc64_machines); + } else if (strcmp(arch, "unicore32") == 0) { + ADD_MACHINE_TESTS(arch, unicore32_machines); + } else if (strcmp(arch, "xtensa") == 0 || + strcmp(arch, "xtensaeb") == 0) { + ADD_MACHINE_TESTS(arch, xtensa_machines); + } + + return g_test_run(); +} diff --git a/tests/rtc-test.c b/tests/rtc-test.c index 3395d7f50b..f1b123fae1 100644 --- a/tests/rtc-test.c +++ b/tests/rtc-test.c @@ -552,7 +552,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); - s = qtest_start("-display none -rtc clock=vm"); + s = qtest_start("-rtc clock=vm"); qtest_irq_intercept_in(s, "ioapic"); qtest_add_func("/rtc/check-time/bcd", bcd_check_time); diff --git a/tests/tcg/openrisc/test_addc.c b/tests/tcg/openrisc/test_addc.c index 05d18f8ce5..a8f756a69b 100644 --- a/tests/tcg/openrisc/test_addc.c +++ b/tests/tcg/openrisc/test_addc.c @@ -7,9 +7,10 @@ int main(void) b = 0x01; c = 0xffffffff; - result = 1; + result = 0; __asm - ("l.addc %0, %1, %2\n\t" + ("l.add r1, r1, r0\n\t" /* clear carry */ + "l.addc %0, %1, %2\n\t" : "=r"(a) : "r"(b), "r"(c) ); @@ -22,7 +23,8 @@ int main(void) c = 0xffffffff; result = 0x80000001; __asm - ("l.addc %0, %1, %2\n\t" + ("l.add r1, r1, r0\n\t" /* clear carry */ + "l.addc %0, %1, %2\n\t" "l.movhi %2, 0x7fff\n\t" "l.ori %2, %2, 0xffff\n\t" "l.addc %0, %1, %2\n\t" diff --git a/tests/tcg/openrisc/test_addic.c b/tests/tcg/openrisc/test_addic.c index 4ba7432521..857aaa1330 100644 --- a/tests/tcg/openrisc/test_addic.c +++ b/tests/tcg/openrisc/test_addic.c @@ -6,9 +6,10 @@ int main(void) int result; a = 1; - result = 0x1; + result = 0x0; __asm - ("l.addic %0, %0, 0xffff\n\t" + ("l.add r1, r1, r0\n\t" /* clear carry */ + "l.addic %0, %0, 0xffff\n\t" : "+r"(a) ); if (a != result) { @@ -16,10 +17,11 @@ int main(void) return -1; } - a = 0x1; + a = -1; result = 0x201; __asm - ("l.addic %0, %0, 0xffff\n\t" + ("l.add r1, r1, r0\n\t" /* clear carry */ + "l.addic %0, %0, 0x1\n\t" "l.ori %0, r0, 0x100\n\t" "l.addic %0, %0, 0x100\n\t" : "+r"(a) diff --git a/tests/test-bitops.c b/tests/test-bitops.c index 4e713e4e00..8238eb5f6b 100644 --- a/tests/test-bitops.c +++ b/tests/test-bitops.c @@ -31,8 +31,8 @@ static const S32Test test_s32_data[] = { }; static const S64Test test_s64_data[] = { - { 0x8459826734967223, 60, 4, -8 }, - { 0x8459826734967223, 0, 64, 0x8459826734967223 }, + { 0x8459826734967223ULL, 60, 4, -8 }, + { 0x8459826734967223ULL, 0, 64, 0x8459826734967223LL }, }; static void test_sextract32(void) diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c index 9f902b597e..ebeee5d589 100644 --- a/tests/test-opts-visitor.c +++ b/tests/test-opts-visitor.c @@ -125,7 +125,7 @@ test_value(OptsVisitorFixture *f, gconstpointer test_data) g_assert((magic & bitval) == 0); magic |= bitval; } - g_assert(magic == 0xBADC0FFEE0DDF00D); + g_assert(magic == 0xBADC0FFEE0DDF00DULL); magic = 0; for (u16 = f->userdef->u16; u16 != NULL; u16 = u16->next) { diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index 0beb8fbfd2..1e1c6fa0c2 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -604,6 +604,7 @@ static void test_visitor_in_errors(TestInputVisitorData *data, g_assert(error_is_set(&errp)); g_assert(p->string == NULL); + error_free(errp); g_free(p->string); g_free(p); } diff --git a/tests/test-throttle.c b/tests/test-throttle.c index 760812645b..1d4ffd3603 100644 --- a/tests/test-throttle.c +++ b/tests/test-throttle.c @@ -18,7 +18,7 @@ LeakyBucket bkt; ThrottleConfig cfg; ThrottleState ts; -/* usefull function */ +/* useful function */ static bool double_cmp(double x, double y) { return fabsl(x - y) < 1e-6; @@ -320,7 +320,7 @@ static void test_have_timer(void) /* zero the structure */ memset(&ts, 0, sizeof(ts)); - /* no timer set shoudl return false */ + /* no timer set should return false */ g_assert(!throttle_have_timer(&ts)); /* init the structure */ diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c index fecd6dcd70..5ac48e2f5c 100644 --- a/tests/tmp105-test.c +++ b/tests/tmp105-test.c @@ -59,7 +59,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); - s = qtest_start("-display none -machine n800"); + s = qtest_start("-machine n800"); i2c = omap_i2c_create(OMAP2_I2C_1_BASE); addr = N8X0_ADDR; diff --git a/ui/console.c b/ui/console.c index aad4fc9a57..199ba69101 100644 --- a/ui/console.c +++ b/ui/console.c @@ -409,39 +409,6 @@ static const pixman_color_t color_table_rgb[2][8] = { } }; -#ifdef DEBUG_CONSOLE -static void console_print_text_attributes(TextAttributes *t_attrib, char ch) -{ - if (t_attrib->bold) { - printf("b"); - } else { - printf(" "); - } - if (t_attrib->uline) { - printf("u"); - } else { - printf(" "); - } - if (t_attrib->blink) { - printf("l"); - } else { - printf(" "); - } - if (t_attrib->invers) { - printf("i"); - } else { - printf(" "); - } - if (t_attrib->unvisible) { - printf("n"); - } else { - printf(" "); - } - - printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch); -} -#endif - static void vga_putcharxy(QemuConsole *s, int x, int y, int ch, TextAttributes *t_attrib) { diff --git a/ui/keymaps.c b/ui/keymaps.c index f373cc53d9..80d658d907 100644 --- a/ui/keymaps.c +++ b/ui/keymaps.c @@ -33,6 +33,12 @@ static int get_keysym(const name2keysym_t *table, if (!strcmp(p->name, name)) return p->keysym; } + if (name[0] == 'U' && strlen(name) == 5) { /* try unicode Uxxxx */ + char *end; + int ret = (int)strtoul(name + 1, &end, 16); + if (*end == '\0' && ret > 0) + return ret; + } return 0; } diff --git a/ui/sdl.c b/ui/sdl.c index 39a42d6b0c..9d8583c4e6 100644 --- a/ui/sdl.c +++ b/ui/sdl.c @@ -86,6 +86,7 @@ static void sdl_update(DisplayChangeListener *dcl, static void do_sdl_resize(int width, int height, int bpp) { int flags; + SDL_Surface *tmp_screen; // printf("resizing to %d %d\n", w, h); @@ -98,12 +99,26 @@ static void do_sdl_resize(int width, int height, int bpp) if (gui_noframe) flags |= SDL_NOFRAME; - real_screen = SDL_SetVideoMode(width, height, bpp, flags); + tmp_screen = SDL_SetVideoMode(width, height, bpp, flags); if (!real_screen) { - fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n", width, - height, bpp, SDL_GetError()); - exit(1); + if (!tmp_screen) { + fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n", + width, height, bpp, SDL_GetError()); + exit(1); + } + } else { + /* + * Revert to the previous video mode if the change of resizing or + * resolution failed. + */ + if (!tmp_screen) { + fprintf(stderr, "Failed to set SDL display (%dx%dx%d): %s\n", + width, height, bpp, SDL_GetError()); + return; + } } + + real_screen = tmp_screen; } static void sdl_switch(DisplayChangeListener *dcl, diff --git a/ui/vnc-enc-zywrle.h b/ui/vnc-enc-zywrle.h index 1ff40b1f40..d436d588fc 100644 --- a/ui/vnc-enc-zywrle.h +++ b/ui/vnc-enc-zywrle.h @@ -305,7 +305,7 @@ static inline void harr(int8_t *px0, int8_t *px1) |L1H0H1H0|L1H0H1H0|L1H0H1H0|L1H0H1H0| : level 1 In this method, H/L and X0/X1 is always same position. - This lead us to more speed and less memory. + This leads us to more speed and less memory. Of cause, the result of both method is quite same because it's only difference that coefficient position. */ diff --git a/ui/vnc_keysym.h b/ui/vnc_keysym.h index 6250bec692..1dc039f71f 100644 --- a/ui/vnc_keysym.h +++ b/ui/vnc_keysym.h @@ -224,6 +224,14 @@ static const name2keysym_t name2keysym[]={ { "odoubleacute", 0x1f5}, { "udoubleacute", 0x1fb}, +/* Czech national characters */ +{ "ecaron", 0x1ec}, +{ "scaron", 0x1b9}, +{ "ccaron", 0x1e8}, +{ "rcaron", 0x1f8}, +{ "zcaron", 0x1be}, +{ "uring", 0x1f9}, + /* modifiers */ {"ISO_Level3_Shift", 0xfe03}, /* XK_ISO_Level3_Shift */ {"Control_L", 0xffe3}, /* XK_Control_L */ @@ -342,5 +350,370 @@ static const name2keysym_t name2keysym[]={ {"Katakana_Real", 0xff25}, {"Eisu_toggle", 0xff30}, +{"abovedot", 0x01ff}, /* U+02D9 DOT ABOVE */ +{"amacron", 0x03e0}, /* U+0101 LATIN SMALL LETTER A WITH MACRON */ +{"Amacron", 0x03c0}, /* U+0100 LATIN CAPITAL LETTER A WITH MACRON */ +{"Arabic_ain", 0x05d9}, /* U+0639 ARABIC LETTER AIN */ +{"Arabic_alef", 0x05c7}, /* U+0627 ARABIC LETTER ALEF */ +{"Arabic_alefmaksura", 0x05e9}, /* U+0649 ARABIC LETTER ALEF MAKSURA */ +{"Arabic_beh", 0x05c8}, /* U+0628 ARABIC LETTER BEH */ +{"Arabic_comma", 0x05ac}, /* U+060C ARABIC COMMA */ +{"Arabic_dad", 0x05d6}, /* U+0636 ARABIC LETTER DAD */ +{"Arabic_dal", 0x05cf}, /* U+062F ARABIC LETTER DAL */ +{"Arabic_damma", 0x05ef}, /* U+064F ARABIC DAMMA */ +{"Arabic_dammatan", 0x05ec}, /* U+064C ARABIC DAMMATAN */ +{"Arabic_fatha", 0x05ee}, /* U+064E ARABIC FATHA */ +{"Arabic_fathatan", 0x05eb}, /* U+064B ARABIC FATHATAN */ +{"Arabic_feh", 0x05e1}, /* U+0641 ARABIC LETTER FEH */ +{"Arabic_ghain", 0x05da}, /* U+063A ARABIC LETTER GHAIN */ +{"Arabic_ha", 0x05e7}, /* U+0647 ARABIC LETTER HEH */ +{"Arabic_hah", 0x05cd}, /* U+062D ARABIC LETTER HAH */ +{"Arabic_hamza", 0x05c1}, /* U+0621 ARABIC LETTER HAMZA */ +{"Arabic_hamzaonalef", 0x05c3}, /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */ +{"Arabic_hamzaonwaw", 0x05c4}, /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */ +{"Arabic_hamzaonyeh", 0x05c6}, /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */ +{"Arabic_hamzaunderalef", 0x05c5}, /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */ +{"Arabic_jeem", 0x05cc}, /* U+062C ARABIC LETTER JEEM */ +{"Arabic_kaf", 0x05e3}, /* U+0643 ARABIC LETTER KAF */ +{"Arabic_kasra", 0x05f0}, /* U+0650 ARABIC KASRA */ +{"Arabic_kasratan", 0x05ed}, /* U+064D ARABIC KASRATAN */ +{"Arabic_khah", 0x05ce}, /* U+062E ARABIC LETTER KHAH */ +{"Arabic_lam", 0x05e4}, /* U+0644 ARABIC LETTER LAM */ +{"Arabic_maddaonalef", 0x05c2}, /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */ +{"Arabic_meem", 0x05e5}, /* U+0645 ARABIC LETTER MEEM */ +{"Arabic_noon", 0x05e6}, /* U+0646 ARABIC LETTER NOON */ +{"Arabic_qaf", 0x05e2}, /* U+0642 ARABIC LETTER QAF */ +{"Arabic_question_mark", 0x05bf}, /* U+061F ARABIC QUESTION MARK */ +{"Arabic_ra", 0x05d1}, /* U+0631 ARABIC LETTER REH */ +{"Arabic_sad", 0x05d5}, /* U+0635 ARABIC LETTER SAD */ +{"Arabic_seen", 0x05d3}, /* U+0633 ARABIC LETTER SEEN */ +{"Arabic_semicolon", 0x05bb}, /* U+061B ARABIC SEMICOLON */ +{"Arabic_shadda", 0x05f1}, /* U+0651 ARABIC SHADDA */ +{"Arabic_sheen", 0x05d4}, /* U+0634 ARABIC LETTER SHEEN */ +{"Arabic_sukun", 0x05f2}, /* U+0652 ARABIC SUKUN */ +{"Arabic_tah", 0x05d7}, /* U+0637 ARABIC LETTER TAH */ +{"Arabic_tatweel", 0x05e0}, /* U+0640 ARABIC TATWEEL */ +{"Arabic_teh", 0x05ca}, /* U+062A ARABIC LETTER TEH */ +{"Arabic_tehmarbuta", 0x05c9}, /* U+0629 ARABIC LETTER TEH MARBUTA */ +{"Arabic_thal", 0x05d0}, /* U+0630 ARABIC LETTER THAL */ +{"Arabic_theh", 0x05cb}, /* U+062B ARABIC LETTER THEH */ +{"Arabic_waw", 0x05e8}, /* U+0648 ARABIC LETTER WAW */ +{"Arabic_yeh", 0x05ea}, /* U+064A ARABIC LETTER YEH */ +{"Arabic_zah", 0x05d8}, /* U+0638 ARABIC LETTER ZAH */ +{"Arabic_zain", 0x05d2}, /* U+0632 ARABIC LETTER ZAIN */ +{"breve", 0x01a2}, /* U+02D8 BREVE */ +{"caron", 0x01b7}, /* U+02C7 CARON */ +{"Ccaron", 0x01c8}, /* U+010C LATIN CAPITAL LETTER C WITH CARON */ +{"Cyrillic_a", 0x06c1}, /* U+0430 CYRILLIC SMALL LETTER A */ +{"Cyrillic_A", 0x06e1}, /* U+0410 CYRILLIC CAPITAL LETTER A */ +{"Cyrillic_be", 0x06c2}, /* U+0431 CYRILLIC SMALL LETTER BE */ +{"Cyrillic_BE", 0x06e2}, /* U+0411 CYRILLIC CAPITAL LETTER BE */ +{"Cyrillic_che", 0x06de}, /* U+0447 CYRILLIC SMALL LETTER CHE */ +{"Cyrillic_CHE", 0x06fe}, /* U+0427 CYRILLIC CAPITAL LETTER CHE */ +{"Cyrillic_de", 0x06c4}, /* U+0434 CYRILLIC SMALL LETTER DE */ +{"Cyrillic_DE", 0x06e4}, /* U+0414 CYRILLIC CAPITAL LETTER DE */ +{"Cyrillic_dzhe", 0x06af}, /* U+045F CYRILLIC SMALL LETTER DZHE */ +{"Cyrillic_DZHE", 0x06bf}, /* U+040F CYRILLIC CAPITAL LETTER DZHE */ +{"Cyrillic_e", 0x06dc}, /* U+044D CYRILLIC SMALL LETTER E */ +{"Cyrillic_E", 0x06fc}, /* U+042D CYRILLIC CAPITAL LETTER E */ +{"Cyrillic_ef", 0x06c6}, /* U+0444 CYRILLIC SMALL LETTER EF */ +{"Cyrillic_EF", 0x06e6}, /* U+0424 CYRILLIC CAPITAL LETTER EF */ +{"Cyrillic_el", 0x06cc}, /* U+043B CYRILLIC SMALL LETTER EL */ +{"Cyrillic_EL", 0x06ec}, /* U+041B CYRILLIC CAPITAL LETTER EL */ +{"Cyrillic_em", 0x06cd}, /* U+043C CYRILLIC SMALL LETTER EM */ +{"Cyrillic_EM", 0x06ed}, /* U+041C CYRILLIC CAPITAL LETTER EM */ +{"Cyrillic_en", 0x06ce}, /* U+043D CYRILLIC SMALL LETTER EN */ +{"Cyrillic_EN", 0x06ee}, /* U+041D CYRILLIC CAPITAL LETTER EN */ +{"Cyrillic_er", 0x06d2}, /* U+0440 CYRILLIC SMALL LETTER ER */ +{"Cyrillic_ER", 0x06f2}, /* U+0420 CYRILLIC CAPITAL LETTER ER */ +{"Cyrillic_es", 0x06d3}, /* U+0441 CYRILLIC SMALL LETTER ES */ +{"Cyrillic_ES", 0x06f3}, /* U+0421 CYRILLIC CAPITAL LETTER ES */ +{"Cyrillic_ghe", 0x06c7}, /* U+0433 CYRILLIC SMALL LETTER GHE */ +{"Cyrillic_GHE", 0x06e7}, /* U+0413 CYRILLIC CAPITAL LETTER GHE */ +{"Cyrillic_ha", 0x06c8}, /* U+0445 CYRILLIC SMALL LETTER HA */ +{"Cyrillic_HA", 0x06e8}, /* U+0425 CYRILLIC CAPITAL LETTER HA */ +{"Cyrillic_hardsign", 0x06df}, /* U+044A CYRILLIC SMALL LETTER HARD SIGN */ +{"Cyrillic_HARDSIGN", 0x06ff}, /* U+042A CYRILLIC CAPITAL LETTER HARD SIGN */ +{"Cyrillic_i", 0x06c9}, /* U+0438 CYRILLIC SMALL LETTER I */ +{"Cyrillic_I", 0x06e9}, /* U+0418 CYRILLIC CAPITAL LETTER I */ +{"Cyrillic_ie", 0x06c5}, /* U+0435 CYRILLIC SMALL LETTER IE */ +{"Cyrillic_IE", 0x06e5}, /* U+0415 CYRILLIC CAPITAL LETTER IE */ +{"Cyrillic_io", 0x06a3}, /* U+0451 CYRILLIC SMALL LETTER IO */ +{"Cyrillic_IO", 0x06b3}, /* U+0401 CYRILLIC CAPITAL LETTER IO */ +{"Cyrillic_je", 0x06a8}, /* U+0458 CYRILLIC SMALL LETTER JE */ +{"Cyrillic_JE", 0x06b8}, /* U+0408 CYRILLIC CAPITAL LETTER JE */ +{"Cyrillic_ka", 0x06cb}, /* U+043A CYRILLIC SMALL LETTER KA */ +{"Cyrillic_KA", 0x06eb}, /* U+041A CYRILLIC CAPITAL LETTER KA */ +{"Cyrillic_lje", 0x06a9}, /* U+0459 CYRILLIC SMALL LETTER LJE */ +{"Cyrillic_LJE", 0x06b9}, /* U+0409 CYRILLIC CAPITAL LETTER LJE */ +{"Cyrillic_nje", 0x06aa}, /* U+045A CYRILLIC SMALL LETTER NJE */ +{"Cyrillic_NJE", 0x06ba}, /* U+040A CYRILLIC CAPITAL LETTER NJE */ +{"Cyrillic_o", 0x06cf}, /* U+043E CYRILLIC SMALL LETTER O */ +{"Cyrillic_O", 0x06ef}, /* U+041E CYRILLIC CAPITAL LETTER O */ +{"Cyrillic_pe", 0x06d0}, /* U+043F CYRILLIC SMALL LETTER PE */ +{"Cyrillic_PE", 0x06f0}, /* U+041F CYRILLIC CAPITAL LETTER PE */ +{"Cyrillic_sha", 0x06db}, /* U+0448 CYRILLIC SMALL LETTER SHA */ +{"Cyrillic_SHA", 0x06fb}, /* U+0428 CYRILLIC CAPITAL LETTER SHA */ +{"Cyrillic_shcha", 0x06dd}, /* U+0449 CYRILLIC SMALL LETTER SHCHA */ +{"Cyrillic_SHCHA", 0x06fd}, /* U+0429 CYRILLIC CAPITAL LETTER SHCHA */ +{"Cyrillic_shorti", 0x06ca}, /* U+0439 CYRILLIC SMALL LETTER SHORT I */ +{"Cyrillic_SHORTI", 0x06ea}, /* U+0419 CYRILLIC CAPITAL LETTER SHORT I */ +{"Cyrillic_softsign", 0x06d8}, /* U+044C CYRILLIC SMALL LETTER SOFT SIGN */ +{"Cyrillic_SOFTSIGN", 0x06f8}, /* U+042C CYRILLIC CAPITAL LETTER SOFT SIGN */ +{"Cyrillic_te", 0x06d4}, /* U+0442 CYRILLIC SMALL LETTER TE */ +{"Cyrillic_TE", 0x06f4}, /* U+0422 CYRILLIC CAPITAL LETTER TE */ +{"Cyrillic_tse", 0x06c3}, /* U+0446 CYRILLIC SMALL LETTER TSE */ +{"Cyrillic_TSE", 0x06e3}, /* U+0426 CYRILLIC CAPITAL LETTER TSE */ +{"Cyrillic_u", 0x06d5}, /* U+0443 CYRILLIC SMALL LETTER U */ +{"Cyrillic_U", 0x06f5}, /* U+0423 CYRILLIC CAPITAL LETTER U */ +{"Cyrillic_ve", 0x06d7}, /* U+0432 CYRILLIC SMALL LETTER VE */ +{"Cyrillic_VE", 0x06f7}, /* U+0412 CYRILLIC CAPITAL LETTER VE */ +{"Cyrillic_ya", 0x06d1}, /* U+044F CYRILLIC SMALL LETTER YA */ +{"Cyrillic_YA", 0x06f1}, /* U+042F CYRILLIC CAPITAL LETTER YA */ +{"Cyrillic_yeru", 0x06d9}, /* U+044B CYRILLIC SMALL LETTER YERU */ +{"Cyrillic_YERU", 0x06f9}, /* U+042B CYRILLIC CAPITAL LETTER YERU */ +{"Cyrillic_yu", 0x06c0}, /* U+044E CYRILLIC SMALL LETTER YU */ +{"Cyrillic_YU", 0x06e0}, /* U+042E CYRILLIC CAPITAL LETTER YU */ +{"Cyrillic_ze", 0x06da}, /* U+0437 CYRILLIC SMALL LETTER ZE */ +{"Cyrillic_ZE", 0x06fa}, /* U+0417 CYRILLIC CAPITAL LETTER ZE */ +{"Cyrillic_zhe", 0x06d6}, /* U+0436 CYRILLIC SMALL LETTER ZHE */ +{"Cyrillic_ZHE", 0x06f6}, /* U+0416 CYRILLIC CAPITAL LETTER ZHE */ +{"doubleacute", 0x01bd}, /* U+02DD DOUBLE ACUTE ACCENT */ +{"doublelowquotemark", 0x0afe}, /* U+201E DOUBLE LOW-9 QUOTATION MARK */ +{"downarrow", 0x08fe}, /* U+2193 DOWNWARDS ARROW */ +{"dstroke", 0x01f0}, /* U+0111 LATIN SMALL LETTER D WITH STROKE */ +{"Dstroke", 0x01d0}, /* U+0110 LATIN CAPITAL LETTER D WITH STROKE */ +{"eabovedot", 0x03ec}, /* U+0117 LATIN SMALL LETTER E WITH DOT ABOVE */ +{"Eabovedot", 0x03cc}, /* U+0116 LATIN CAPITAL LETTER E WITH DOT ABOVE */ +{"emacron", 0x03ba}, /* U+0113 LATIN SMALL LETTER E WITH MACRON */ +{"Emacron", 0x03aa}, /* U+0112 LATIN CAPITAL LETTER E WITH MACRON */ +{"endash", 0x0aaa}, /* U+2013 EN DASH */ +{"eng", 0x03bf}, /* U+014B LATIN SMALL LETTER ENG */ +{"ENG", 0x03bd}, /* U+014A LATIN CAPITAL LETTER ENG */ +{"Execute", 0xff62}, /* Execute, run, do */ +{"F16", 0xffcd}, +{"F17", 0xffce}, +{"F18", 0xffcf}, +{"F19", 0xffd0}, +{"F20", 0xffd1}, +{"F21", 0xffd2}, +{"F22", 0xffd3}, +{"F23", 0xffd4}, +{"F24", 0xffd5}, +{"F25", 0xffd6}, +{"F26", 0xffd7}, +{"F27", 0xffd8}, +{"F28", 0xffd9}, +{"F29", 0xffda}, +{"F30", 0xffdb}, +{"F31", 0xffdc}, +{"F32", 0xffdd}, +{"F33", 0xffde}, +{"F34", 0xffdf}, +{"F35", 0xffe0}, +{"fiveeighths", 0x0ac5}, /* U+215D VULGAR FRACTION FIVE EIGHTHS */ +{"gbreve", 0x02bb}, /* U+011F LATIN SMALL LETTER G WITH BREVE */ +{"Gbreve", 0x02ab}, /* U+011E LATIN CAPITAL LETTER G WITH BREVE */ +{"gcedilla", 0x03bb}, /* U+0123 LATIN SMALL LETTER G WITH CEDILLA */ +{"Gcedilla", 0x03ab}, /* U+0122 LATIN CAPITAL LETTER G WITH CEDILLA */ +{"Greek_OMEGA", 0x07d9}, /* U+03A9 GREEK CAPITAL LETTER OMEGA */ +{"Henkan_Mode", 0xff23}, /* Start/Stop Conversion */ +{"horizconnector", 0x08a3}, /*(U+2500 BOX DRAWINGS LIGHT HORIZONTAL)*/ +{"hstroke", 0x02b1}, /* U+0127 LATIN SMALL LETTER H WITH STROKE */ +{"Hstroke", 0x02a1}, /* U+0126 LATIN CAPITAL LETTER H WITH STROKE */ +{"Iabovedot", 0x02a9}, /* U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE */ +{"idotless", 0x02b9}, /* U+0131 LATIN SMALL LETTER DOTLESS I */ +{"imacron", 0x03ef}, /* U+012B LATIN SMALL LETTER I WITH MACRON */ +{"Imacron", 0x03cf}, /* U+012A LATIN CAPITAL LETTER I WITH MACRON */ +{"iogonek", 0x03e7}, /* U+012F LATIN SMALL LETTER I WITH OGONEK */ +{"Iogonek", 0x03c7}, /* U+012E LATIN CAPITAL LETTER I WITH OGONEK */ +{"ISO_First_Group", 0xfe0c}, +{"ISO_Last_Group", 0xfe0e}, +{"ISO_Next_Group", 0xfe08}, +{"kana_a", 0x04a7}, /* U+30A1 KATAKANA LETTER SMALL A */ +{"kana_A", 0x04b1}, /* U+30A2 KATAKANA LETTER A */ +{"kana_CHI", 0x04c1}, /* U+30C1 KATAKANA LETTER TI */ +{"kana_closingbracket", 0x04a3}, /* U+300D RIGHT CORNER BRACKET */ +{"kana_comma", 0x04a4}, /* U+3001 IDEOGRAPHIC COMMA */ +{"kana_conjunctive", 0x04a5}, /* U+30FB KATAKANA MIDDLE DOT */ +{"kana_e", 0x04aa}, /* U+30A7 KATAKANA LETTER SMALL E */ +{"kana_E", 0x04b4}, /* U+30A8 KATAKANA LETTER E */ +{"kana_FU", 0x04cc}, /* U+30D5 KATAKANA LETTER HU */ +{"kana_fullstop", 0x04a1}, /* U+3002 IDEOGRAPHIC FULL STOP */ +{"kana_HA", 0x04ca}, /* U+30CF KATAKANA LETTER HA */ +{"kana_HE", 0x04cd}, /* U+30D8 KATAKANA LETTER HE */ +{"kana_HI", 0x04cb}, /* U+30D2 KATAKANA LETTER HI */ +{"kana_HO", 0x04ce}, /* U+30DB KATAKANA LETTER HO */ +{"kana_i", 0x04a8}, /* U+30A3 KATAKANA LETTER SMALL I */ +{"kana_I", 0x04b2}, /* U+30A4 KATAKANA LETTER I */ +{"kana_KA", 0x04b6}, /* U+30AB KATAKANA LETTER KA */ +{"kana_KE", 0x04b9}, /* U+30B1 KATAKANA LETTER KE */ +{"kana_KI", 0x04b7}, /* U+30AD KATAKANA LETTER KI */ +{"kana_KO", 0x04ba}, /* U+30B3 KATAKANA LETTER KO */ +{"kana_KU", 0x04b8}, /* U+30AF KATAKANA LETTER KU */ +{"kana_MA", 0x04cf}, /* U+30DE KATAKANA LETTER MA */ +{"kana_ME", 0x04d2}, /* U+30E1 KATAKANA LETTER ME */ +{"kana_MI", 0x04d0}, /* U+30DF KATAKANA LETTER MI */ +{"kana_MO", 0x04d3}, /* U+30E2 KATAKANA LETTER MO */ +{"kana_MU", 0x04d1}, /* U+30E0 KATAKANA LETTER MU */ +{"kana_N", 0x04dd}, /* U+30F3 KATAKANA LETTER N */ +{"kana_NA", 0x04c5}, /* U+30CA KATAKANA LETTER NA */ +{"kana_NE", 0x04c8}, /* U+30CD KATAKANA LETTER NE */ +{"kana_NI", 0x04c6}, /* U+30CB KATAKANA LETTER NI */ +{"kana_NO", 0x04c9}, /* U+30CE KATAKANA LETTER NO */ +{"kana_NU", 0x04c7}, /* U+30CC KATAKANA LETTER NU */ +{"kana_o", 0x04ab}, /* U+30A9 KATAKANA LETTER SMALL O */ +{"kana_O", 0x04b5}, /* U+30AA KATAKANA LETTER O */ +{"kana_openingbracket", 0x04a2}, /* U+300C LEFT CORNER BRACKET */ +{"kana_RA", 0x04d7}, /* U+30E9 KATAKANA LETTER RA */ +{"kana_RE", 0x04da}, /* U+30EC KATAKANA LETTER RE */ +{"kana_RI", 0x04d8}, /* U+30EA KATAKANA LETTER RI */ +{"kana_RU", 0x04d9}, /* U+30EB KATAKANA LETTER RU */ +{"kana_SA", 0x04bb}, /* U+30B5 KATAKANA LETTER SA */ +{"kana_SE", 0x04be}, /* U+30BB KATAKANA LETTER SE */ +{"kana_SHI", 0x04bc}, /* U+30B7 KATAKANA LETTER SI */ +{"kana_SO", 0x04bf}, /* U+30BD KATAKANA LETTER SO */ +{"kana_SU", 0x04bd}, /* U+30B9 KATAKANA LETTER SU */ +{"kana_TA", 0x04c0}, /* U+30BF KATAKANA LETTER TA */ +{"kana_TE", 0x04c3}, /* U+30C6 KATAKANA LETTER TE */ +{"kana_TO", 0x04c4}, /* U+30C8 KATAKANA LETTER TO */ +{"kana_tsu", 0x04af}, /* U+30C3 KATAKANA LETTER SMALL TU */ +{"kana_TSU", 0x04c2}, /* U+30C4 KATAKANA LETTER TU */ +{"kana_u", 0x04a9}, /* U+30A5 KATAKANA LETTER SMALL U */ +{"kana_U", 0x04b3}, /* U+30A6 KATAKANA LETTER U */ +{"kana_WA", 0x04dc}, /* U+30EF KATAKANA LETTER WA */ +{"kana_WO", 0x04a6}, /* U+30F2 KATAKANA LETTER WO */ +{"kana_ya", 0x04ac}, /* U+30E3 KATAKANA LETTER SMALL YA */ +{"kana_YA", 0x04d4}, /* U+30E4 KATAKANA LETTER YA */ +{"kana_yo", 0x04ae}, /* U+30E7 KATAKANA LETTER SMALL YO */ +{"kana_YO", 0x04d6}, /* U+30E8 KATAKANA LETTER YO */ +{"kana_yu", 0x04ad}, /* U+30E5 KATAKANA LETTER SMALL YU */ +{"kana_YU", 0x04d5}, /* U+30E6 KATAKANA LETTER YU */ +{"Kanji", 0xff21}, /* Kanji, Kanji convert */ +{"kcedilla", 0x03f3}, /* U+0137 LATIN SMALL LETTER K WITH CEDILLA */ +{"Kcedilla", 0x03d3}, /* U+0136 LATIN CAPITAL LETTER K WITH CEDILLA */ +{"kra", 0x03a2}, /* U+0138 LATIN SMALL LETTER KRA */ +{"lcedilla", 0x03b6}, /* U+013C LATIN SMALL LETTER L WITH CEDILLA */ +{"Lcedilla", 0x03a6}, /* U+013B LATIN CAPITAL LETTER L WITH CEDILLA */ +{"leftarrow", 0x08fb}, /* U+2190 LEFTWARDS ARROW */ +{"leftdoublequotemark", 0x0ad2}, /* U+201C LEFT DOUBLE QUOTATION MARK */ +{"Macedonia_dse", 0x06a5}, /* U+0455 CYRILLIC SMALL LETTER DZE */ +{"Macedonia_DSE", 0x06b5}, /* U+0405 CYRILLIC CAPITAL LETTER DZE */ +{"Macedonia_gje", 0x06a2}, /* U+0453 CYRILLIC SMALL LETTER GJE */ +{"Macedonia_GJE", 0x06b2}, /* U+0403 CYRILLIC CAPITAL LETTER GJE */ +{"Macedonia_kje", 0x06ac}, /* U+045C CYRILLIC SMALL LETTER KJE */ +{"Macedonia_KJE", 0x06bc}, /* U+040C CYRILLIC CAPITAL LETTER KJE */ +{"ncedilla", 0x03f1}, /* U+0146 LATIN SMALL LETTER N WITH CEDILLA */ +{"Ncedilla", 0x03d1}, /* U+0145 LATIN CAPITAL LETTER N WITH CEDILLA */ +{"oe", 0x13bd}, /* U+0153 LATIN SMALL LIGATURE OE */ +{"OE", 0x13bc}, /* U+0152 LATIN CAPITAL LIGATURE OE */ +{"ogonek", 0x01b2}, /* U+02DB OGONEK */ +{"omacron", 0x03f2}, /* U+014D LATIN SMALL LETTER O WITH MACRON */ +{"Omacron", 0x03d2}, /* U+014C LATIN CAPITAL LETTER O WITH MACRON */ +{"oneeighth", 0x0ac3}, /* U+215B VULGAR FRACTION ONE EIGHTH */ +{"rcedilla", 0x03b3}, /* U+0157 LATIN SMALL LETTER R WITH CEDILLA */ +{"Rcedilla", 0x03a3}, /* U+0156 LATIN CAPITAL LETTER R WITH CEDILLA */ +{"rightarrow", 0x08fd}, /* U+2192 RIGHTWARDS ARROW */ +{"rightdoublequotemark", 0x0ad3}, /* U+201D RIGHT DOUBLE QUOTATION MARK */ +{"Scaron", 0x01a9}, /* U+0160 LATIN CAPITAL LETTER S WITH CARON */ +{"scedilla", 0x01ba}, /* U+015F LATIN SMALL LETTER S WITH CEDILLA */ +{"Scedilla", 0x01aa}, /* U+015E LATIN CAPITAL LETTER S WITH CEDILLA */ +{"semivoicedsound", 0x04df}, /* U+309C KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ +{"seveneighths", 0x0ac6}, /* U+215E VULGAR FRACTION SEVEN EIGHTHS */ +{"Thai_baht", 0x0ddf}, /* U+0E3F THAI CURRENCY SYMBOL BAHT */ +{"Thai_bobaimai", 0x0dba}, /* U+0E1A THAI CHARACTER BO BAIMAI */ +{"Thai_chochan", 0x0da8}, /* U+0E08 THAI CHARACTER CHO CHAN */ +{"Thai_chochang", 0x0daa}, /* U+0E0A THAI CHARACTER CHO CHANG */ +{"Thai_choching", 0x0da9}, /* U+0E09 THAI CHARACTER CHO CHING */ +{"Thai_chochoe", 0x0dac}, /* U+0E0C THAI CHARACTER CHO CHOE */ +{"Thai_dochada", 0x0dae}, /* U+0E0E THAI CHARACTER DO CHADA */ +{"Thai_dodek", 0x0db4}, /* U+0E14 THAI CHARACTER DO DEK */ +{"Thai_fofa", 0x0dbd}, /* U+0E1D THAI CHARACTER FO FA */ +{"Thai_fofan", 0x0dbf}, /* U+0E1F THAI CHARACTER FO FAN */ +{"Thai_hohip", 0x0dcb}, /* U+0E2B THAI CHARACTER HO HIP */ +{"Thai_honokhuk", 0x0dce}, /* U+0E2E THAI CHARACTER HO NOKHUK */ +{"Thai_khokhai", 0x0da2}, /* U+0E02 THAI CHARACTER KHO KHAI */ +{"Thai_khokhon", 0x0da5}, /* U+0E05 THAI CHARACTER KHO KHON */ +{"Thai_khokhuat", 0x0da3}, /* U+0E03 THAI CHARACTER KHO KHUAT */ +{"Thai_khokhwai", 0x0da4}, /* U+0E04 THAI CHARACTER KHO KHWAI */ +{"Thai_khorakhang", 0x0da6}, /* U+0E06 THAI CHARACTER KHO RAKHANG */ +{"Thai_kokai", 0x0da1}, /* U+0E01 THAI CHARACTER KO KAI */ +{"Thai_lakkhangyao", 0x0de5}, /* U+0E45 THAI CHARACTER LAKKHANGYAO */ +{"Thai_lekchet", 0x0df7}, /* U+0E57 THAI DIGIT SEVEN */ +{"Thai_lekha", 0x0df5}, /* U+0E55 THAI DIGIT FIVE */ +{"Thai_lekhok", 0x0df6}, /* U+0E56 THAI DIGIT SIX */ +{"Thai_lekkao", 0x0df9}, /* U+0E59 THAI DIGIT NINE */ +{"Thai_leknung", 0x0df1}, /* U+0E51 THAI DIGIT ONE */ +{"Thai_lekpaet", 0x0df8}, /* U+0E58 THAI DIGIT EIGHT */ +{"Thai_leksam", 0x0df3}, /* U+0E53 THAI DIGIT THREE */ +{"Thai_leksi", 0x0df4}, /* U+0E54 THAI DIGIT FOUR */ +{"Thai_leksong", 0x0df2}, /* U+0E52 THAI DIGIT TWO */ +{"Thai_leksun", 0x0df0}, /* U+0E50 THAI DIGIT ZERO */ +{"Thai_lochula", 0x0dcc}, /* U+0E2C THAI CHARACTER LO CHULA */ +{"Thai_loling", 0x0dc5}, /* U+0E25 THAI CHARACTER LO LING */ +{"Thai_lu", 0x0dc6}, /* U+0E26 THAI CHARACTER LU */ +{"Thai_maichattawa", 0x0deb}, /* U+0E4B THAI CHARACTER MAI CHATTAWA */ +{"Thai_maiek", 0x0de8}, /* U+0E48 THAI CHARACTER MAI EK */ +{"Thai_maihanakat", 0x0dd1}, /* U+0E31 THAI CHARACTER MAI HAN-AKAT */ +{"Thai_maitaikhu", 0x0de7}, /* U+0E47 THAI CHARACTER MAITAIKHU */ +{"Thai_maitho", 0x0de9}, /* U+0E49 THAI CHARACTER MAI THO */ +{"Thai_maitri", 0x0dea}, /* U+0E4A THAI CHARACTER MAI TRI */ +{"Thai_maiyamok", 0x0de6}, /* U+0E46 THAI CHARACTER MAIYAMOK */ +{"Thai_moma", 0x0dc1}, /* U+0E21 THAI CHARACTER MO MA */ +{"Thai_ngongu", 0x0da7}, /* U+0E07 THAI CHARACTER NGO NGU */ +{"Thai_nikhahit", 0x0ded}, /* U+0E4D THAI CHARACTER NIKHAHIT */ +{"Thai_nonen", 0x0db3}, /* U+0E13 THAI CHARACTER NO NEN */ +{"Thai_nonu", 0x0db9}, /* U+0E19 THAI CHARACTER NO NU */ +{"Thai_oang", 0x0dcd}, /* U+0E2D THAI CHARACTER O ANG */ +{"Thai_paiyannoi", 0x0dcf}, /* U+0E2F THAI CHARACTER PAIYANNOI */ +{"Thai_phinthu", 0x0dda}, /* U+0E3A THAI CHARACTER PHINTHU */ +{"Thai_phophan", 0x0dbe}, /* U+0E1E THAI CHARACTER PHO PHAN */ +{"Thai_phophung", 0x0dbc}, /* U+0E1C THAI CHARACTER PHO PHUNG */ +{"Thai_phosamphao", 0x0dc0}, /* U+0E20 THAI CHARACTER PHO SAMPHAO */ +{"Thai_popla", 0x0dbb}, /* U+0E1B THAI CHARACTER PO PLA */ +{"Thai_rorua", 0x0dc3}, /* U+0E23 THAI CHARACTER RO RUA */ +{"Thai_ru", 0x0dc4}, /* U+0E24 THAI CHARACTER RU */ +{"Thai_saraa", 0x0dd0}, /* U+0E30 THAI CHARACTER SARA A */ +{"Thai_saraaa", 0x0dd2}, /* U+0E32 THAI CHARACTER SARA AA */ +{"Thai_saraae", 0x0de1}, /* U+0E41 THAI CHARACTER SARA AE */ +{"Thai_saraaimaimalai", 0x0de4}, /* U+0E44 THAI CHARACTER SARA AI MAIMALAI */ +{"Thai_saraaimaimuan", 0x0de3}, /* U+0E43 THAI CHARACTER SARA AI MAIMUAN */ +{"Thai_saraam", 0x0dd3}, /* U+0E33 THAI CHARACTER SARA AM */ +{"Thai_sarae", 0x0de0}, /* U+0E40 THAI CHARACTER SARA E */ +{"Thai_sarai", 0x0dd4}, /* U+0E34 THAI CHARACTER SARA I */ +{"Thai_saraii", 0x0dd5}, /* U+0E35 THAI CHARACTER SARA II */ +{"Thai_sarao", 0x0de2}, /* U+0E42 THAI CHARACTER SARA O */ +{"Thai_sarau", 0x0dd8}, /* U+0E38 THAI CHARACTER SARA U */ +{"Thai_saraue", 0x0dd6}, /* U+0E36 THAI CHARACTER SARA UE */ +{"Thai_sarauee", 0x0dd7}, /* U+0E37 THAI CHARACTER SARA UEE */ +{"Thai_sarauu", 0x0dd9}, /* U+0E39 THAI CHARACTER SARA UU */ +{"Thai_sorusi", 0x0dc9}, /* U+0E29 THAI CHARACTER SO RUSI */ +{"Thai_sosala", 0x0dc8}, /* U+0E28 THAI CHARACTER SO SALA */ +{"Thai_soso", 0x0dab}, /* U+0E0B THAI CHARACTER SO SO */ +{"Thai_sosua", 0x0dca}, /* U+0E2A THAI CHARACTER SO SUA */ +{"Thai_thanthakhat", 0x0dec}, /* U+0E4C THAI CHARACTER THANTHAKHAT */ +{"Thai_thonangmontho", 0x0db1}, /* U+0E11 THAI CHARACTER THO NANGMONTHO */ +{"Thai_thophuthao", 0x0db2}, /* U+0E12 THAI CHARACTER THO PHUTHAO */ +{"Thai_thothahan", 0x0db7}, /* U+0E17 THAI CHARACTER THO THAHAN */ +{"Thai_thothan", 0x0db0}, /* U+0E10 THAI CHARACTER THO THAN */ +{"Thai_thothong", 0x0db8}, /* U+0E18 THAI CHARACTER THO THONG */ +{"Thai_thothung", 0x0db6}, /* U+0E16 THAI CHARACTER THO THUNG */ +{"Thai_topatak", 0x0daf}, /* U+0E0F THAI CHARACTER TO PATAK */ +{"Thai_totao", 0x0db5}, /* U+0E15 THAI CHARACTER TO TAO */ +{"Thai_wowaen", 0x0dc7}, /* U+0E27 THAI CHARACTER WO WAEN */ +{"Thai_yoyak", 0x0dc2}, /* U+0E22 THAI CHARACTER YO YAK */ +{"Thai_yoying", 0x0dad}, /* U+0E0D THAI CHARACTER YO YING */ +{"threeeighths", 0x0ac4}, /* U+215C VULGAR FRACTION THREE EIGHTHS */ +{"trademark", 0x0ac9}, /* U+2122 TRADE MARK SIGN */ +{"tslash", 0x03bc}, /* U+0167 LATIN SMALL LETTER T WITH STROKE */ +{"Tslash", 0x03ac}, /* U+0166 LATIN CAPITAL LETTER T WITH STROKE */ +{"umacron", 0x03fe}, /* U+016B LATIN SMALL LETTER U WITH MACRON */ +{"Umacron", 0x03de}, /* U+016A LATIN CAPITAL LETTER U WITH MACRON */ +{"uogonek", 0x03f9}, /* U+0173 LATIN SMALL LETTER U WITH OGONEK */ +{"Uogonek", 0x03d9}, /* U+0172 LATIN CAPITAL LETTER U WITH OGONEK */ +{"uparrow", 0x08fc}, /* U+2191 UPWARDS ARROW */ +{"voicedsound", 0x04de}, /* U+309B KATAKANA-HIRAGANA VOICED SOUND MARK */ +{"Zcaron", 0x01ae}, /* U+017D LATIN CAPITAL LETTER Z WITH CARON */ + {NULL,0}, }; diff --git a/util/qemu-config.c b/util/qemu-config.c index a59568d070..04da942a25 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -8,6 +8,7 @@ #include "qmp-commands.h" static QemuOptsList *vm_config_groups[32]; +static QemuOptsList *drive_config_groups[4]; static QemuOptsList *find_list(QemuOptsList **lists, const char *group, Error **errp) @@ -77,6 +78,59 @@ static CommandLineParameterInfoList *query_option_descs(const QemuOptDesc *desc) return param_list; } +/* remove repeated entry from the info list */ +static void cleanup_infolist(CommandLineParameterInfoList *head) +{ + CommandLineParameterInfoList *pre_entry, *cur, *del_entry; + + cur = head; + while (cur->next) { + pre_entry = head; + while (pre_entry != cur->next) { + if (!strcmp(pre_entry->value->name, cur->next->value->name)) { + del_entry = cur->next; + cur->next = cur->next->next; + g_free(del_entry); + break; + } + pre_entry = pre_entry->next; + } + cur = cur->next; + } +} + +/* merge the description items of two parameter infolists */ +static void connect_infolist(CommandLineParameterInfoList *head, + CommandLineParameterInfoList *new) +{ + CommandLineParameterInfoList *cur; + + cur = head; + while (cur->next) { + cur = cur->next; + } + cur->next = new; +} + +/* access all the local QemuOptsLists for drive option */ +static CommandLineParameterInfoList *get_drive_infolist(void) +{ + CommandLineParameterInfoList *head = NULL, *cur; + int i; + + for (i = 0; drive_config_groups[i] != NULL; i++) { + if (!head) { + head = query_option_descs(drive_config_groups[i]->desc); + } else { + cur = query_option_descs(drive_config_groups[i]->desc); + connect_infolist(head, cur); + } + } + cleanup_infolist(head); + + return head; +} + CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option, const char *option, Error **errp) @@ -89,7 +143,12 @@ CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option, if (!has_option || !strcmp(option, vm_config_groups[i]->name)) { info = g_malloc0(sizeof(*info)); info->option = g_strdup(vm_config_groups[i]->name); - info->parameters = query_option_descs(vm_config_groups[i]->desc); + if (!strcmp("drive", vm_config_groups[i]->name)) { + info->parameters = get_drive_infolist(); + } else { + info->parameters = + query_option_descs(vm_config_groups[i]->desc); + } entry = g_malloc0(sizeof(*entry)); entry->value = info; entry->next = conf_list; @@ -109,6 +168,22 @@ QemuOptsList *qemu_find_opts_err(const char *group, Error **errp) return find_list(vm_config_groups, group, errp); } +void qemu_add_drive_opts(QemuOptsList *list) +{ + int entries, i; + + entries = ARRAY_SIZE(drive_config_groups); + entries--; /* keep list NULL terminated */ + for (i = 0; i < entries; i++) { + if (drive_config_groups[i] == NULL) { + drive_config_groups[i] = list; + return; + } + } + fprintf(stderr, "ran out of space in drive_config_groups"); + abort(); +} + void qemu_add_opts(QemuOptsList *list) { int entries, i; diff --git a/vl.c b/vl.c index b42ac67100..8d5d874e68 100644 --- a/vl.c +++ b/vl.c @@ -638,9 +638,8 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_WATCHDOG, RUN_STATE_RUNNING }, { RUN_STATE_WATCHDOG, RUN_STATE_FINISH_MIGRATE }, - { RUN_STATE_GUEST_PANICKED, RUN_STATE_PAUSED }, + { RUN_STATE_GUEST_PANICKED, RUN_STATE_RUNNING }, { RUN_STATE_GUEST_PANICKED, RUN_STATE_FINISH_MIGRATE }, - { RUN_STATE_GUEST_PANICKED, RUN_STATE_DEBUG }, { RUN_STATE_MAX, RUN_STATE_MAX }, }; @@ -686,8 +685,7 @@ int runstate_is_running(void) bool runstate_needs_reset(void) { return runstate_check(RUN_STATE_INTERNAL_ERROR) || - runstate_check(RUN_STATE_SHUTDOWN) || - runstate_check(RUN_STATE_GUEST_PANICKED); + runstate_check(RUN_STATE_SHUTDOWN); } StatusInfo *qmp_query_status(Error **errp) @@ -2869,6 +2867,9 @@ int main(int argc, char **argv, char **envp) module_call_init(MODULE_INIT_QOM); qemu_add_opts(&qemu_drive_opts); + qemu_add_drive_opts(&qemu_legacy_drive_opts); + qemu_add_drive_opts(&qemu_common_drive_opts); + qemu_add_drive_opts(&qemu_drive_opts); qemu_add_opts(&qemu_chardev_opts); qemu_add_opts(&qemu_device_opts); qemu_add_opts(&qemu_netdev_opts); @@ -4269,6 +4270,7 @@ int main(int argc, char **argv, char **envp) /* init local displays */ switch (display_type) { case DT_NOGRAPHIC: + (void)ds; /* avoid warning if no display is configured */ break; #if defined(CONFIG_CURSES) case DT_CURSES: @@ -4336,6 +4338,9 @@ int main(int argc, char **argv, char **envp) qemu_register_reset(qbus_reset_all_fn, sysbus_get_default()); qemu_run_machine_init_done_notifiers(); + /* Done notifiers can load ROMs */ + rom_load_done(); + qemu_system_reset(VMRESET_SILENT); if (loadvm) { if (load_vmstate(loadvm) < 0) { |