diff options
81 files changed, 3413 insertions, 3580 deletions
diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000000..9797802aaa --- /dev/null +++ b/.mailmap @@ -0,0 +1,16 @@ +# This mailmap just translates the weird addresses from the original import into git +# into proper addresses so that they are counted properly in git shortlog output. +# +Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162> +Anthony Liguori <aliguori@us.ibm.com> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> +Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162> +Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162> +Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162> +Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> +Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> +Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> +Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162> +malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162> +# There is also a: +# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> +# for the cvs2svn initialization commit e63c3dc74bf. @@ -169,7 +169,7 @@ test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y) $(qapi-obj-y): $(GENERATED_HEADERS) qapi-dir := $(BUILD_DIR)/qapi-generated -test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir) +test-qmp-input-visitor.o test-qmp-output-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir) qemu-ga$(EXESUF): LIBS = $(LIBS_QGA) $(qapi-dir)/test-qapi-types.c $(qapi-dir)/test-qapi-types.h :\ @@ -202,8 +202,11 @@ qmp-commands.h qmp-marshal.c :\ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -m -o "." < $<, " GEN $@") -test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) -test-visitor: test-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o +test-qmp-output-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) +test-qmp-output-visitor: test-qmp-output-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o + +test-qmp-input-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y) +test-qmp-input-visitor: test-qmp-input-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y) test-qmp-commands: test-qmp-commands.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o diff --git a/Makefile.target b/Makefile.target index 7369a89e55..8be9b9a596 100644 --- a/Makefile.target +++ b/Makefile.target @@ -342,6 +342,7 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o obj-arm-y += versatile_pci.o obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o +obj-arm-y += arm_mptimer.o obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o obj-arm-y += pl061.o obj-arm-y += arm-semi.o @@ -359,9 +360,6 @@ obj-arm-y += mst_fpga.o mainstone.o obj-arm-y += z2.o obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o obj-arm-y += framebuffer.o -obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o -obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o -obj-arm-y += syborg_virtio.o obj-arm-y += vexpress.o obj-arm-y += strongarm.o obj-arm-y += collie.o diff --git a/QMP/qmp b/QMP/qmp new file mode 100755 index 0000000000..1db3c7ffeb --- /dev/null +++ b/QMP/qmp @@ -0,0 +1,126 @@ +#!/usr/bin/python +# +# QMP command line tool +# +# Copyright IBM, Corp. 2011 +# +# Authors: +# Anthony Liguori <aliguori@us.ibm.com> +# +# This work is licensed under the terms of the GNU GPLv2 or later. +# See the COPYING file in the top-level directory. + +import sys, os +from qmp import QEMUMonitorProtocol + +def print_response(rsp, prefix=[]): + if type(rsp) == list: + i = 0 + for item in rsp: + if prefix == []: + prefix = ['item'] + print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)]) + i += 1 + elif type(rsp) == dict: + for key in rsp.keys(): + print_response(rsp[key], prefix + [key]) + else: + if len(prefix): + print '%s: %s' % ('.'.join(prefix), rsp) + else: + print '%s' % (rsp) + +def main(args): + path = None + + # Use QMP_PATH if it's set + if os.environ.has_key('QMP_PATH'): + path = os.environ['QMP_PATH'] + + while len(args): + arg = args[0] + + if arg.startswith('--'): + arg = arg[2:] + if arg.find('=') == -1: + value = True + else: + arg, value = arg.split('=', 1) + + if arg in ['path']: + if type(value) == str: + path = value + elif arg in ['help']: + os.execlp('man', 'man', 'qmp') + else: + print 'Unknown argument "%s"' % arg + + args = args[1:] + else: + break + + if not path: + print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH" + return 1 + + if len(args): + command, args = args[0], args[1:] + else: + print 'No command found' + print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"' + return 1 + + if command in ['help']: + os.execlp('man', 'man', 'qmp') + + srv = QEMUMonitorProtocol(path) + srv.connect() + + def do_command(srv, cmd, **kwds): + rsp = srv.cmd(cmd, kwds) + if rsp.has_key('error'): + raise Exception(rsp['error']['desc']) + return rsp['return'] + + commands = map(lambda x: x['name'], do_command(srv, 'query-commands')) + + srv.close() + + if command not in commands: + fullcmd = 'qmp-%s' % command + try: + os.environ['QMP_PATH'] = path + os.execvp(fullcmd, [fullcmd] + args) + except OSError, (errno, msg): + if errno == 2: + print 'Command "%s" not found.' % (fullcmd) + return 1 + raise + return 0 + + srv = QEMUMonitorProtocol(path) + srv.connect() + + arguments = {} + for arg in args: + if not arg.startswith('--'): + print 'Unknown argument "%s"' % arg + return 1 + + arg = arg[2:] + if arg.find('=') == -1: + value = True + else: + arg, value = arg.split('=', 1) + + if arg in ['help']: + os.execlp('man', 'man', 'qmp-%s' % command) + return 1 + + arguments[arg] = value + + rsp = do_command(srv, command, **arguments) + print_response(rsp) + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) @@ -1,3 +1,3 @@ -Read the documentation in qemu-doc.html. +Read the documentation in qemu-doc.html or on http://wiki.qemu.org -Fabrice Bellard. +- QEMU team @@ -100,31 +100,19 @@ BalloonInfo *qmp_query_balloon(Error **errp) return info; } -/** - * do_balloon(): Request VM to change its memory allocation - */ -int do_balloon(Monitor *mon, const QDict *params, - MonitorCompletion cb, void *opaque) +void qmp_balloon(int64_t value, Error **errp) { - int64_t target; - int ret; - if (kvm_enabled() && !kvm_has_sync_mmu()) { - qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); - return -1; + error_set(errp, QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); + return; } - target = qdict_get_int(params, "value"); - if (target <= 0) { + if (value <= 0) { qerror_report(QERR_INVALID_PARAMETER_VALUE, "target", "a size"); - return -1; + return; } - ret = qemu_balloon(target); - if (ret == 0) { - qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon"); - return -1; + + if (qemu_balloon(value) == 0) { + error_set(errp, QERR_DEVICE_NOT_ACTIVE, "balloon"); } - - cb(opaque, NULL); - return 0; } @@ -24,7 +24,4 @@ int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, QEMUBalloonStatus *stat_func, void *opaque); void qemu_remove_balloon_handler(void *opaque); -int do_balloon(Monitor *mon, const QDict *params, - MonitorCompletion cb, void *opaque); - #endif diff --git a/blockdev.c b/blockdev.c index dbf0251a77..c832782d03 100644 --- a/blockdev.c +++ b/blockdev.c @@ -15,6 +15,7 @@ #include "qemu-config.h" #include "sysemu.h" #include "block_int.h" +#include "qmp-commands.h" static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); @@ -600,28 +601,20 @@ void do_commit(Monitor *mon, const QDict *qdict) } } -int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file, + bool has_format, const char *format, + Error **errp) { - const char *device = qdict_get_str(qdict, "device"); - const char *filename = qdict_get_try_str(qdict, "snapshot-file"); - const char *format = qdict_get_try_str(qdict, "format"); BlockDriverState *bs; BlockDriver *drv, *old_drv, *proto_drv; int ret = 0; int flags; char old_filename[1024]; - if (!filename) { - qerror_report(QERR_MISSING_PARAMETER, "snapshot-file"); - ret = -1; - goto out; - } - bs = bdrv_find(device); if (!bs) { - qerror_report(QERR_DEVICE_NOT_FOUND, device); - ret = -1; - goto out; + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; } pstrcpy(old_filename, sizeof(old_filename), bs->filename); @@ -629,35 +622,34 @@ int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data) old_drv = bs->drv; flags = bs->open_flags; - if (!format) { + if (!has_format) { format = "qcow2"; } drv = bdrv_find_format(format); if (!drv) { - qerror_report(QERR_INVALID_BLOCK_FORMAT, format); - ret = -1; - goto out; + error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); + return; } - proto_drv = bdrv_find_protocol(filename); + proto_drv = bdrv_find_protocol(snapshot_file); if (!proto_drv) { - qerror_report(QERR_INVALID_BLOCK_FORMAT, format); - ret = -1; - goto out; + error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); + return; } - ret = bdrv_img_create(filename, format, bs->filename, + ret = bdrv_img_create(snapshot_file, format, bs->filename, bs->drv->format_name, NULL, -1, flags); if (ret) { - goto out; + error_set(errp, QERR_UNDEFINED_ERROR); + return; } bdrv_drain_all(); bdrv_flush(bs); bdrv_close(bs); - ret = bdrv_open(bs, filename, flags, drv); + ret = bdrv_open(bs, snapshot_file, flags, drv); /* * If reopening the image file we just created fails, fall back * and try to re-open the original image. If that fails too, we @@ -666,17 +658,11 @@ int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data) if (ret != 0) { ret = bdrv_open(bs, old_filename, flags, old_drv); if (ret != 0) { - qerror_report(QERR_OPEN_FILE_FAILED, old_filename); + error_set(errp, QERR_OPEN_FILE_FAILED, old_filename); } else { - qerror_report(QERR_OPEN_FILE_FAILED, filename); + error_set(errp, QERR_OPEN_FILE_FAILED, snapshot_file); } } -out: - if (ret) { - ret = -1; - } - - return ret; } static int eject_device(Monitor *mon, BlockDriverState *bs, int force) @@ -710,28 +696,25 @@ int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data) return eject_device(mon, bs, force); } -int do_block_set_passwd(Monitor *mon, const QDict *qdict, - QObject **ret_data) +void qmp_block_passwd(const char *device, const char *password, Error **errp) { BlockDriverState *bs; int err; - bs = bdrv_find(qdict_get_str(qdict, "device")); + bs = bdrv_find(device); if (!bs) { - qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device")); - return -1; + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; } - err = bdrv_set_key(bs, qdict_get_str(qdict, "password")); + err = bdrv_set_key(bs, password); if (err == -EINVAL) { - qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs)); - return -1; + error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs)); + return; } else if (err < 0) { - qerror_report(QERR_INVALID_PASSWORD); - return -1; + error_set(errp, QERR_INVALID_PASSWORD); + return; } - - return 0; } int do_change_block(Monitor *mon, const char *device, @@ -863,27 +846,23 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) * existing QERR_ macro mess is cleaned up. A good example for better * error reports can be found in the qemu-img resize code. */ -int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_block_resize(const char *device, int64_t size, Error **errp) { - const char *device = qdict_get_str(qdict, "device"); - int64_t size = qdict_get_int(qdict, "size"); BlockDriverState *bs; bs = bdrv_find(device); if (!bs) { - qerror_report(QERR_DEVICE_NOT_FOUND, device); - return -1; + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; } if (size < 0) { - qerror_report(QERR_UNDEFINED_ERROR); - return -1; + error_set(errp, QERR_UNDEFINED_ERROR); + return; } if (bdrv_truncate(bs, size)) { - qerror_report(QERR_UNDEFINED_ERROR); - return -1; + error_set(errp, QERR_UNDEFINED_ERROR); + return; } - - return 0; } diff --git a/blockdev.h b/blockdev.h index 1b48a75499..f1b639660d 100644 --- a/blockdev.h +++ b/blockdev.h @@ -59,13 +59,9 @@ DriveInfo *add_init_drive(const char *opts); void do_commit(Monitor *mon, const QDict *qdict); int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data); -int do_block_set_passwd(Monitor *mon, const QDict *qdict, QObject **ret_data); int do_change_block(Monitor *mon, const char *device, const char *filename, const char *fmt); int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); int do_block_set_io_throttle(Monitor *mon, const QDict *qdict, QObject **ret_data); -int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data); -int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data); - #endif diff --git a/bsd-user/bsdload.c b/bsd-user/bsdload.c index 6d9bb6fb4e..2abc7136e0 100644 --- a/bsd-user/bsdload.c +++ b/bsd-user/bsdload.c @@ -196,7 +196,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp, /* Something went wrong, return the inode and free the argument pages*/ for (i=0 ; i<MAX_ARG_PAGES ; i++) { - free(bprm.page[i]); + g_free(bprm.page[i]); } return(retval); } diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index 1ef1f972fc..12888840a4 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -641,8 +641,7 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page, offset = p % TARGET_PAGE_SIZE; pag = (char *)page[p/TARGET_PAGE_SIZE]; if (!pag) { - pag = (char *)malloc(TARGET_PAGE_SIZE); - memset(pag, 0, TARGET_PAGE_SIZE); + pag = g_try_malloc0(TARGET_PAGE_SIZE); page[p/TARGET_PAGE_SIZE] = pag; if (!pag) return 0; @@ -696,7 +695,7 @@ static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, info->rss++; /* FIXME - check return value of memcpy_to_target() for failure */ memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE); - free(bprm->page[i]); + g_free(bprm->page[i]); } stack_base += TARGET_PAGE_SIZE; } @@ -1116,7 +1116,7 @@ fi if test "$pie" = ""; then case "$cpu-$targetos" in - i386-Linux|x86_64-Linux) + i386-Linux|x86_64-Linux|i386-OpenBSD|x86_64-OpenBSD) ;; *) pie="no" @@ -1528,9 +1528,6 @@ EOF if compile_prog "$sdl_cflags" "$sdl_libs" ; then sdl_libs="$sdl_libs -lX11" fi - if test "$mingw32" = "yes" ; then - sdl_libs="`echo $sdl_libs | sed s/-mwindows//g` -mconsole" - fi libs_softmmu="$sdl_libs $libs_softmmu" fi @@ -2748,8 +2745,9 @@ if test "$softmmu" = yes ; then fi if [ "$check_utests" = "yes" ]; then checks="check-qint check-qstring check-qdict check-qlist" - checks="check-qfloat check-qjson test-coroutine $checks" + checks="check-qfloat check-qjson $checks" fi + test_progs="$checks test-coroutine test-qmp-output-visitor test-qmp-input-visitor" fi fi @@ -3235,7 +3233,7 @@ if test "$trace_default" = "yes"; then fi echo "TOOLS=$tools" >> $config_host_mak -echo "CHECKS=$checks" >> $config_host_mak +echo "CHECKS=$test_progs" >> $config_host_mak echo "ROMS=$roms" >> $config_host_mak echo "MAKE=$make" >> $config_host_mak echo "INSTALL=$install" >> $config_host_mak @@ -74,8 +74,6 @@ struct MouseTransformInfo { int a[7]; }; -void do_info_mice_print(Monitor *mon, const QObject *data); -void do_info_mice(Monitor *mon, QObject **ret_data); void do_mouse_set(Monitor *mon, const QDict *qdict); /* keysym is a unicode code except for special keys (see QEMU_KEY_xxx @@ -910,7 +910,8 @@ static void qemu_tcg_init_vcpu(void *_env) env->halt_cond = g_malloc0(sizeof(QemuCond)); qemu_cond_init(env->halt_cond); tcg_halt_cond = env->halt_cond; - qemu_thread_create(env->thread, qemu_tcg_cpu_thread_fn, env); + qemu_thread_create(env->thread, qemu_tcg_cpu_thread_fn, env, + QEMU_THREAD_DETACHED); while (env->created == 0) { qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); } @@ -926,7 +927,8 @@ static void qemu_kvm_start_vcpu(CPUState *env) env->thread = g_malloc0(sizeof(QemuThread)); env->halt_cond = g_malloc0(sizeof(QemuCond)); qemu_cond_init(env->halt_cond); - qemu_thread_create(env->thread, qemu_kvm_cpu_thread_fn, env); + qemu_thread_create(env->thread, qemu_kvm_cpu_thread_fn, env, + QEMU_THREAD_DETACHED); while (env->created == 0) { qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex); } @@ -1136,3 +1138,93 @@ CpuInfoList *qmp_query_cpus(Error **errp) return head; } + +void qmp_memsave(int64_t addr, int64_t size, const char *filename, + bool has_cpu, int64_t cpu_index, Error **errp) +{ + FILE *f; + uint32_t l; + CPUState *env; + uint8_t buf[1024]; + + if (!has_cpu) { + cpu_index = 0; + } + + for (env = first_cpu; env; env = env->next_cpu) { + if (cpu_index == env->cpu_index) { + break; + } + } + + if (env == NULL) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + return; + } + + f = fopen(filename, "wb"); + if (!f) { + error_set(errp, QERR_OPEN_FILE_FAILED, filename); + return; + } + + while (size != 0) { + l = sizeof(buf); + if (l > size) + l = size; + cpu_memory_rw_debug(env, addr, buf, l, 0); + if (fwrite(buf, 1, l, f) != l) { + error_set(errp, QERR_IO_ERROR); + goto exit; + } + addr += l; + size -= l; + } + +exit: + fclose(f); +} + +void qmp_pmemsave(int64_t addr, int64_t size, const char *filename, + Error **errp) +{ + FILE *f; + uint32_t l; + uint8_t buf[1024]; + + f = fopen(filename, "wb"); + if (!f) { + error_set(errp, QERR_OPEN_FILE_FAILED, filename); + return; + } + + while (size != 0) { + l = sizeof(buf); + if (l > size) + l = size; + cpu_physical_memory_rw(addr, buf, l, 0); + if (fwrite(buf, 1, l, f) != l) { + error_set(errp, QERR_IO_ERROR); + goto exit; + } + addr += l; + size -= l; + } + +exit: + fclose(f); +} + +void qmp_inject_nmi(Error **errp) +{ +#if defined(TARGET_I386) + CPUState *env; + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + cpu_interrupt(env, CPU_INTERRUPT_NMI); + } +#else + error_set(errp, QERR_UNSUPPORTED); +#endif +} diff --git a/docs/writing-qmp-commands.txt b/docs/writing-qmp-commands.txt new file mode 100644 index 0000000000..0472fc3914 --- /dev/null +++ b/docs/writing-qmp-commands.txt @@ -0,0 +1,642 @@ += How to write QMP commands using the QAPI framework = + +This document is a step-by-step guide on how to write new QMP commands using +the QAPI framework. It also shows how to implement new style HMP commands. + +This document doesn't discuss QMP protocol level details, nor does it dive +into the QAPI framework implementation. + +For an in-depth introduction to the QAPI framework, please refer to +docs/qapi-code-gen.txt. For documentation about the QMP protocol, please +check the files in QMP/. + +== Overview == + +Generally speaking, the following steps should be taken in order to write a +new QMP command. + +1. Write the command's and type(s) specification in the QAPI schema file + (qapi-schema.json in the root source directory) + +2. Write the QMP command itself, which is a regular C function. Preferably, + the command should be exported by some QEMU subsystem. But it can also be + added to the qmp.c file + +3. At this point the command can be tested under the QMP protocol + +4. Write the HMP command equivalent. This is not required and should only be + done if it does make sense to have the functionality in HMP. The HMP command + is implemented in terms of the QMP command + +The following sections will demonstrate each of the steps above. We will start +very simple and get more complex as we progress. + +=== Testing === + +For all the examples in the next sections, the test setup is the same and is +shown here. + +First, QEMU should be started as: + +# /path/to/your/source/qemu [...] \ + -chardev socket,id=qmp,port=4444,host=localhost,server \ + -mon chardev=qmp,mode=control,pretty=on + +Then, in a different terminal: + +$ telnet localhost 4444 +Trying 127.0.0.1... +Connected to localhost. +Escape character is '^]'. +{ + "QMP": { + "version": { + "qemu": { + "micro": 50, + "minor": 15, + "major": 0 + }, + "package": "" + }, + "capabilities": [ + ] + } +} + +The above output is the QMP server saying you're connected. The server is +actually in capabilities negotiation mode. To enter in command mode type: + +{ "execute": "qmp_capabilities" } + +Then the server should respond: + +{ + "return": { + } +} + +Which is QMP's way of saying "the latest command executed OK and didn't return +any data". Now you're ready to enter the QMP example commands as explained in +the following sections. + +== Writing a command that doesn't return data == + +That's the most simple QMP command that can be written. Usually, this kind of +command carries some meaningful action in QEMU but here it will just print +"Hello, world" to the standard output. + +Our command will be called "hello-world". It takes no arguments, nor does it +return any data. + +The first step is to add the following line to the bottom of the +qapi-schema.json file: + +{ 'command': 'hello-world' } + +The "command" keyword defines a new QMP command. It's an JSON object. All +schema entries are JSON objects. The line above will instruct the QAPI to +generate any prototypes and the necessary code to marshal and unmarshal +protocol data. + +The next step is to write the "hello-world" implementation. As explained +earlier, it's preferable for commands to live in QEMU subsystems. But +"hello-world" doesn't pertain to any, so we put its implementation in qmp.c: + +void qmp_hello_world(Error **errp) +{ + printf("Hello, world!\n"); +} + +There are a few things to be noticed: + +1. QMP command implementation functions must be prefixed with "qmp_" +2. qmp_hello_world() returns void, this is in accordance with the fact that the + command doesn't return any data +3. It takes an "Error **" argument. This is required. Later we will see how to + return errors and take additional arguments. The Error argument should not + be touched if the command doesn't return errors +4. We won't add the function's prototype. That's automatically done by the QAPI +5. Printing to the terminal is discouraged for QMP commands, we do it here + because it's the easiest way to demonstrate a QMP command + +Now a little hack is needed. As we're still using the old QMP server we need +to add the new command to its internal dispatch table. This step won't be +required in the near future. Open the qmp-commands.hx file and add the +following in the botton: + + { + .name = "hello-world", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_hello_world, + }, + +You're done. Now build qemu, run it as suggested in the "Testing" section, +and then type the following QMP command: + +{ "execute": "hello-world" } + +Then check the terminal running qemu and look for the "Hello, world" string. If +you don't see it then something went wrong. + +=== Arguments === + +Let's add an argument called "message" to our "hello-world" command. The new +argument will contain the string to be printed to stdout. It's an optional +argument, if it's not present we print our default "Hello, World" string. + +The first change we have to do is to modify the command specification in the +schema file to the following: + +{ 'command': 'hello-world', 'data': { '*message': 'str' } } + +Notice the new 'data' member in the schema. It's an JSON object whose each +element is an argument to the command in question. Also notice the asterisk, +it's used to mark the argument optional (that means that you shouldn't use it +for mandatory arguments). Finally, 'str' is the argument's type, which +stands for "string". The QAPI also supports integers, booleans, enumerations +and user defined types. + +Now, let's update our C implementation in qmp.c: + +void qmp_hello_world(bool has_message, const char *message, Error **errp) +{ + if (has_message) { + printf("%s\n", message); + } else { + printf("Hello, world\n"); + } +} + +There are two important details to be noticed: + +1. All optional arguments are accompanied by a 'has_' boolean, which is set + if the optional argument is present or false otherwise +2. The C implementation signature must follow the schema's argument ordering, + which is defined by the "data" member + +The last step is to update the qmp-commands.hx file: + + { + .name = "hello-world", + .args_type = "message:s?", + .mhandler.cmd_new = qmp_marshal_input_hello_world, + }, + +Notice that the "args_type" member got our "message" argument. The character +"s" stands for "string" and "?" means it's optional. This too must be ordered +according to the C implementation and schema file. You can look for more +examples in the qmp-commands.hx file if you need to define more arguments. + +Again, this step won't be required in the future. + +Time to test our new version of the "hello-world" command. Build qemu, run it as +described in the "Testing" section and then send two commands: + +{ "execute": "hello-world" } +{ + "return": { + } +} + +{ "execute": "hello-world", "arguments": { "message": "We love qemu" } } +{ + "return": { + } +} + +You should see "Hello, world" and "we love qemu" in the terminal running qemu, +if you don't see these strings, then something went wrong. + +=== Errors === + +QMP commands should use the error interface exported by the error.h header +file. The basic function used to set an error is the error_set() one. + +Let's say we don't accept the string "message" to contain the word "love". If +it does contain it, we want the "hello-world" command to the return the +InvalidParameter error. + +Only one change is required, and it's in the C implementation: + +void qmp_hello_world(bool has_message, const char *message, Error **errp) +{ + if (has_message) { + if (strstr(message, "love")) { + error_set(errp, QERR_INVALID_PARAMETER, "message"); + return; + } + printf("%s\n", message); + } else { + printf("Hello, world\n"); + } +} + +Let's test it. Build qemu, run it as defined in the "Testing" section, and +then issue the following command: + +{ "execute": "hello-world", "arguments": { "message": "we love qemu" } } + +The QMP server's response should be: + +{ + "error": { + "class": "InvalidParameter", + "desc": "Invalid parameter 'message'", + "data": { + "name": "message" + } + } +} + +Which is the InvalidParameter error. + +When you have to return an error but you're unsure what error to return or +which arguments an error takes, you should look at the qerror.h file. Note +that you might be required to add new errors if needed. + +FIXME: describe better the error API and how to add new errors. + +=== Command Documentation === + +There's only one step missing to make "hello-world"'s implementation complete, +and that's its documentation in the schema file. + +This is very important. No QMP command will be accepted in QEMU without proper +documentation. + +There are many examples of such documentation in the schema file already, but +here goes "hello-world"'s new entry for the qapi-schema.json file: + +## +# @hello-world +# +# Print a client provided string to the standard output stream. +# +# @message: #optional string to be printed +# +# Returns: Nothing on success. +# If @message contains "love", InvalidParameter +# +# Notes: if @message is not provided, the "Hello, world" string will +# be printed instead +# +# Since: <next qemu stable release, eg. 1.0> +## +{ 'command': 'hello-world', 'data': { '*message': 'str' } } + +Please, note that the "Returns" clause is optional if a command doesn't return +any data nor any errors. + +=== Implementing the HMP command === + +Now that the QMP command is in place, we can also make it available in the human +monitor (HMP). + +With the introduction of the QAPI, HMP commands make QMP calls. Most of the +time HMP commands are simple wrappers. All HMP commands implementation exist in +the hmp.c file. + +Here's the implementation of the "hello-world" HMP command: + +void hmp_hello_world(Monitor *mon, const QDict *qdict) +{ + const char *message = qdict_get_try_str(qdict, "message"); + Error *errp = NULL; + + qmp_hello_world(!!message, message, &errp); + if (error_is_set(&errp)) { + monitor_printf(mon, "%s\n", error_get_pretty(errp)); + error_free(errp); + return; + } +} + +Also, you have to add the function's prototype to the hmp.h file. + +There are three important points to be noticed: + +1. The "mon" and "qdict" arguments are mandatory for all HMP functions. The + former is the monitor object. The latter is how the monitor passes + arguments entered by the user to the command implementation +2. hmp_hello_world() performs error checking. In this example we just print + the error description to the user, but we could do more, like taking + different actions depending on the error qmp_hello_world() returns +3. The "errp" variable must be initialized to NULL before performing the + QMP call + +There's one last step to actually make the command available to monitor users, +we should add it to the hmp-commands.hx file: + + { + .name = "hello-world", + .args_type = "message:s?", + .params = "hello-world [message]", + .help = "Print message to the standard output", + .mhandler.cmd = hmp_hello_world, + }, + +STEXI +@item hello_world @var{message} +@findex hello_world +Print message to the standard output +ETEXI + +To test this you have to open a user monitor and issue the "hello-world" +command. It might be instructive to check the command's documentation with +HMP's "help" command. + +Please, check the "-monitor" command-line option to know how to open a user +monitor. + +== Writing a command that returns data == + +A QMP command is capable of returning any data the QAPI supports like integers, +strings, booleans, enumerations and user defined types. + +In this section we will focus on user defined types. Please, check the QAPI +documentation for information about the other types. + +=== User Defined Types === + +For this example we will write the query-alarm-clock command, which returns +information about QEMU's timer alarm. For more information about it, please +check the "-clock" command-line option. + +We want to return two pieces of information. The first one is the alarm clock's +name. The second one is when the next alarm will fire. The former information is +returned as a string, the latter is an integer in nanoseconds (which is not +very useful in practice, as the timer has probably already fired when the +information reaches the client). + +The best way to return that data is to create a new QAPI type, as shown below: + +## +# @QemuAlarmClock +# +# QEMU alarm clock information. +# +# @clock-name: The alarm clock method's name. +# +# @next-deadline: #optional The time (in nanoseconds) the next alarm will fire. +# +# Since: 1.0 +## +{ 'type': 'QemuAlarmClock', + 'data': { 'clock-name': 'str', '*next-deadline': 'int' } } + +The "type" keyword defines a new QAPI type. Its "data" member contains the +type's members. In this example our members are the "clock-name" and the +"next-deadline" one, which is optional. + +Now let's define the query-alarm-clock command: + +## +# @query-alarm-clock +# +# Return information about QEMU's alarm clock. +# +# Returns a @QemuAlarmClock instance describing the alarm clock method +# being currently used by QEMU (this is usually set by the '-clock' +# command-line option). +# +# Since: 1.0 +## +{ 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' } + +Notice the "returns" keyword. As its name suggests, it's used to define the +data returned by a command. + +It's time to implement the qmp_query_alarm_clock() function, you can put it +in the qemu-timer.c file: + +QemuAlarmClock *qmp_query_alarm_clock(Error **errp) +{ + QemuAlarmClock *clock; + int64_t deadline; + + clock = g_malloc0(sizeof(*clock)); + + deadline = qemu_next_alarm_deadline(); + if (deadline > 0) { + clock->has_next_deadline = true; + clock->next_deadline = deadline; + } + clock->clock_name = g_strdup(alarm_timer->name); + + return clock; +} + +There are a number of things to be noticed: + +1. The QemuAlarmClock type is automatically generated by the QAPI framework, + its members correspond to the type's specification in the schema file +2. As specified in the schema file, the function returns a QemuAlarmClock + instance and takes no arguments (besides the "errp" one, which is mandatory + for all QMP functions) +3. The "clock" variable (which will point to our QAPI type instance) is + allocated by the regular g_malloc0() function. Note that we chose to + initialize the memory to zero. This is recomended for all QAPI types, as + it helps avoiding bad surprises (specially with booleans) +4. Remember that "next_deadline" is optional? All optional members have a + 'has_TYPE_NAME' member that should be properly set by the implementation, + as shown above +5. Even static strings, such as "alarm_timer->name", should be dynamically + allocated by the implementation. This is so because the QAPI also generates + a function to free its types and it cannot distinguish between dynamically + or statically allocated strings +6. You have to include the "qmp-commands.h" header file in qemu-timer.c, + otherwise qemu won't build + +The last step is to add the correspoding entry in the qmp-commands.hx file: + + { + .name = "query-alarm-clock", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_alarm_clock, + }, + +Time to test the new command. Build qemu, run it as described in the "Testing" +section and try this: + +{ "execute": "query-alarm-clock" } +{ + "return": { + "next-deadline": 2368219, + "clock-name": "dynticks" + } +} + +==== The HMP command ==== + +Here's the HMP counterpart of the query-alarm-clock command: + +void hmp_info_alarm_clock(Monitor *mon) +{ + QemuAlarmClock *clock; + Error *errp = NULL; + + clock = qmp_query_alarm_clock(&errp); + if (error_is_set(&errp)) { + monitor_printf(mon, "Could not query alarm clock information\n"); + error_free(errp); + return; + } + + monitor_printf(mon, "Alarm clock method in use: '%s'\n", clock->clock_name); + if (clock->has_next_deadline) { + monitor_printf(mon, "Next alarm will fire in %" PRId64 " nanoseconds\n", + clock->next_deadline); + } + + qapi_free_QemuAlarmClock(clock); +} + +It's important to notice that hmp_info_alarm_clock() calls +qapi_free_QemuAlarmClock() to free the data returned by qmp_query_alarm_clock(). +For user defined types, the QAPI will generate a qapi_free_QAPI_TYPE_NAME() +function and that's what you have to use to free the types you define and +qapi_free_QAPI_TYPE_NAMEList() for list types (explained in the next section). +If the QMP call returns a string, then you should g_free() to free it. + +Also note that hmp_info_alarm_clock() performs error handling. That's not +strictly required if you're sure the QMP function doesn't return errors, but +it's good practice to always check for errors. + +Another important detail is that HMP's "info" commands don't go into the +hmp-commands.hx. Instead, they go into the info_cmds[] table, which is defined +in the monitor.c file. The entry for the "info alarmclock" follows: + + { + .name = "alarmclock", + .args_type = "", + .params = "", + .help = "show information about the alarm clock", + .mhandler.info = hmp_info_alarm_clock, + }, + +To test this, run qemu and type "info alarmclock" in the user monitor. + +=== Returning Lists === + +For this example, we're going to return all available methods for the timer +alarm, which is pretty much what the command-line option "-clock ?" does, +except that we're also going to inform which method is in use. + +This first step is to define a new type: + +## +# @TimerAlarmMethod +# +# Timer alarm method information. +# +# @method-name: The method's name. +# +# @current: true if this alarm method is currently in use, false otherwise +# +# Since: 1.0 +## +{ 'type': 'TimerAlarmMethod', + 'data': { 'method-name': 'str', 'current': 'bool' } } + +The command will be called "query-alarm-methods", here is its schema +specification: + +## +# @query-alarm-methods +# +# Returns information about available alarm methods. +# +# Returns: a list of @TimerAlarmMethod for each method +# +# Since: 1.0 +## +{ 'command': 'query-alarm-methods', 'returns': ['TimerAlarmMethod'] } + +Notice the syntax for returning lists "'returns': ['TimerAlarmMethod']", this +should be read as "returns a list of TimerAlarmMethod instances". + +The C implementation follows: + +TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp) +{ + TimerAlarmMethodList *method_list = NULL; + const struct qemu_alarm_timer *p; + bool current = true; + + for (p = alarm_timers; p->name; p++) { + TimerAlarmMethodList *info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->method_name = g_strdup(p->name); + info->value->current = current; + + current = false; + + info->next = method_list; + method_list = info; + } + + return method_list; +} + +The most important difference from the previous examples is the +TimerAlarmMethodList type, which is automatically generated by the QAPI from +the TimerAlarmMethod type. + +Each list node is represented by a TimerAlarmMethodList instance. We have to +allocate it, and that's done inside the for loop: the "info" pointer points to +an allocated node. We also have to allocate the node's contents, which is +stored in its "value" member. In our example, the "value" member is a pointer +to an TimerAlarmMethod instance. + +Notice that the "current" variable is used as "true" only in the first +interation of the loop. That's because the alarm timer method in use is the +first element of the alarm_timers array. Also notice that QAPI lists are handled +by hand and we return the head of the list. + +To test this you have to add the corresponding qmp-commands.hx entry: + + { + .name = "query-alarm-methods", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_alarm_methods, + }, + +Now Build qemu, run it as explained in the "Testing" section and try our new +command: + +{ "execute": "query-alarm-methods" } +{ + "return": [ + { + "current": false, + "method-name": "unix" + }, + { + "current": true, + "method-name": "dynticks" + } + ] +} + +The HMP counterpart is a bit more complex than previous examples because it +has to traverse the list, it's shown below for reference: + +void hmp_info_alarm_methods(Monitor *mon) +{ + TimerAlarmMethodList *method_list, *method; + Error *errp = NULL; + + method_list = qmp_query_alarm_methods(&errp); + if (error_is_set(&errp)) { + monitor_printf(mon, "Could not query alarm methods\n"); + error_free(errp); + return; + } + + for (method = method_list; method; method = method->next) { + monitor_printf(mon, "%c %s\n", method->value->current ? '*' : ' ', + method->value->method_name); + } + + qapi_free_TimerAlarmMethodList(method_list); +} @@ -1603,8 +1603,10 @@ void cpu_set_log(int log_flags) static char logfile_buf[4096]; setvbuf(logfile, logfile_buf, _IOLBF, sizeof(logfile_buf)); } -#elif !defined(_WIN32) - /* Win32 doesn't support line-buffering and requires size >= 2 */ +#elif defined(_WIN32) + /* Win32 doesn't support line-buffering, so use unbuffered output. */ + setvbuf(logfile, NULL, _IONBF, 0); +#else setvbuf(logfile, NULL, _IOLBF, 0); #endif log_append = 1; diff --git a/hmp-commands.hx b/hmp-commands.hx index 54b2abf458..14838b7fae 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -57,8 +57,7 @@ ETEXI .args_type = "device:B,size:o", .params = "device size", .help = "resize a block image", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_block_resize, + .mhandler.cmd = hmp_block_resize, }, STEXI @@ -304,8 +303,7 @@ ETEXI .args_type = "", .params = "", .help = "resume emulation", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_cont, + .mhandler.cmd = hmp_cont, }, STEXI @@ -689,8 +687,7 @@ ETEXI .args_type = "val:l,size:i,filename:s", .params = "addr size file", .help = "save to disk virtual memory dump starting at 'addr' of size 'size'", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_memory_save, + .mhandler.cmd = hmp_memsave, }, STEXI @@ -704,8 +701,7 @@ ETEXI .args_type = "val:l,size:i,filename:s", .params = "addr size file", .help = "save to disk physical memory dump starting at 'addr' of size 'size'", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_physical_memory_save, + .mhandler.cmd = hmp_pmemsave, }, STEXI @@ -739,8 +735,7 @@ ETEXI .args_type = "", .params = "", .help = "inject an NMI on all guest's CPUs", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_inject_nmi, + .mhandler.cmd = hmp_inject_nmi, }, #endif STEXI @@ -776,8 +771,7 @@ ETEXI .args_type = "", .params = "", .help = "cancel the current VM migration", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_migrate_cancel, + .mhandler.cmd = hmp_migrate_cancel, }, STEXI @@ -792,8 +786,7 @@ ETEXI .params = "value", .help = "set maximum speed (in bytes) for migrations. " "Defaults to MB if no size suffix is specified, ie. B/K/M/G/T", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_migrate_set_speed, + .mhandler.cmd = hmp_migrate_set_speed, }, STEXI @@ -807,8 +800,7 @@ ETEXI .args_type = "value:T", .params = "value", .help = "set maximum tolerated downtime (in seconds) for migrations", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_migrate_set_downtime, + .mhandler.cmd = hmp_migrate_set_downtime, }, STEXI @@ -845,7 +837,7 @@ ETEXI "If format is specified, the snapshot file will\n\t\t\t" "be created in that format. Otherwise the\n\t\t\t" "snapshot will be internal! (currently unsupported)", - .mhandler.cmd_new = do_snapshot_blkdev, + .mhandler.cmd = hmp_snapshot_blkdev, }, STEXI @@ -1026,9 +1018,7 @@ ETEXI .args_type = "value:M", .params = "target", .help = "request VM to change its memory allocation (in MB)", - .user_print = monitor_user_noop, - .mhandler.cmd_async = do_balloon, - .flags = MONITOR_CMD_ASYNC, + .mhandler.cmd = hmp_balloon, }, STEXI @@ -1042,8 +1032,7 @@ ETEXI .args_type = "name:s,up:b", .params = "name on|off", .help = "change the link status of a network adapter", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_set_link, + .mhandler.cmd = hmp_set_link, }, STEXI @@ -1203,8 +1192,7 @@ ETEXI .args_type = "device:B,password:s", .params = "block_passwd device password", .help = "set the password of encrypted block devices", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_block_set_passwd, + .mhandler.cmd = hmp_block_passwd, }, STEXI @@ -14,6 +14,14 @@ #include "hmp.h" #include "qmp-commands.h" +static void hmp_handle_error(Monitor *mon, Error **errp) +{ + if (error_is_set(errp)) { + monitor_printf(mon, "%s\n", error_get_pretty(*errp)); + error_free(*errp); + } +} + void hmp_info_name(Monitor *mon) { NameInfo *info; @@ -531,3 +539,143 @@ void hmp_cpu(Monitor *mon, const QDict *qdict) monitor_printf(mon, "invalid CPU index\n"); } } + +void hmp_memsave(Monitor *mon, const QDict *qdict) +{ + uint32_t size = qdict_get_int(qdict, "size"); + const char *filename = qdict_get_str(qdict, "filename"); + uint64_t addr = qdict_get_int(qdict, "val"); + Error *errp = NULL; + + qmp_memsave(addr, size, filename, true, monitor_get_cpu_index(), &errp); + hmp_handle_error(mon, &errp); +} + +void hmp_pmemsave(Monitor *mon, const QDict *qdict) +{ + uint32_t size = qdict_get_int(qdict, "size"); + const char *filename = qdict_get_str(qdict, "filename"); + uint64_t addr = qdict_get_int(qdict, "val"); + Error *errp = NULL; + + qmp_pmemsave(addr, size, filename, &errp); + hmp_handle_error(mon, &errp); +} + +static void hmp_cont_cb(void *opaque, int err) +{ + Monitor *mon = opaque; + + if (!err) { + hmp_cont(mon, NULL); + } +} + +void hmp_cont(Monitor *mon, const QDict *qdict) +{ + Error *errp = NULL; + + qmp_cont(&errp); + if (error_is_set(&errp)) { + if (error_is_type(errp, QERR_DEVICE_ENCRYPTED)) { + const char *device; + + /* The device is encrypted. Ask the user for the password + and retry */ + + device = error_get_field(errp, "device"); + assert(device != NULL); + + monitor_read_block_device_key(mon, device, hmp_cont_cb, mon); + error_free(errp); + return; + } + hmp_handle_error(mon, &errp); + } +} + +void hmp_inject_nmi(Monitor *mon, const QDict *qdict) +{ + Error *errp = NULL; + + qmp_inject_nmi(&errp); + hmp_handle_error(mon, &errp); +} + +void hmp_set_link(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + int up = qdict_get_bool(qdict, "up"); + Error *errp = NULL; + + qmp_set_link(name, up, &errp); + hmp_handle_error(mon, &errp); +} + +void hmp_block_passwd(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *password = qdict_get_str(qdict, "password"); + Error *errp = NULL; + + qmp_block_passwd(device, password, &errp); + hmp_handle_error(mon, &errp); +} + +void hmp_balloon(Monitor *mon, const QDict *qdict) +{ + int64_t value = qdict_get_int(qdict, "value"); + Error *errp = NULL; + + qmp_balloon(value, &errp); + if (error_is_set(&errp)) { + monitor_printf(mon, "balloon: %s\n", error_get_pretty(errp)); + error_free(errp); + } +} + +void hmp_block_resize(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + int64_t size = qdict_get_int(qdict, "size"); + Error *errp = NULL; + + qmp_block_resize(device, size, &errp); + hmp_handle_error(mon, &errp); +} + +void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *filename = qdict_get_try_str(qdict, "snapshot-file"); + const char *format = qdict_get_try_str(qdict, "format"); + Error *errp = NULL; + + if (!filename) { + /* In the future, if 'snapshot-file' is not specified, the snapshot + will be taken internally. Today it's actually required. */ + error_set(&errp, QERR_MISSING_PARAMETER, "snapshot-file"); + hmp_handle_error(mon, &errp); + return; + } + + qmp_blockdev_snapshot_sync(device, filename, !!format, format, &errp); + hmp_handle_error(mon, &errp); +} + +void hmp_migrate_cancel(Monitor *mon, const QDict *qdict) +{ + qmp_migrate_cancel(NULL); +} + +void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict) +{ + double value = qdict_get_double(qdict, "value"); + qmp_migrate_set_downtime(value, NULL); +} + +void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict) +{ + int64_t value = qdict_get_int(qdict, "value"); + qmp_migrate_set_speed(value, NULL); +} @@ -37,5 +37,17 @@ void hmp_stop(Monitor *mon, const QDict *qdict); void hmp_system_reset(Monitor *mon, const QDict *qdict); void hmp_system_powerdown(Monitor *mon, const QDict *qdict); void hmp_cpu(Monitor *mon, const QDict *qdict); +void hmp_memsave(Monitor *mon, const QDict *qdict); +void hmp_pmemsave(Monitor *mon, const QDict *qdict); +void hmp_cont(Monitor *mon, const QDict *qdict); +void hmp_inject_nmi(Monitor *mon, const QDict *qdict); +void hmp_set_link(Monitor *mon, const QDict *qdict); +void hmp_block_passwd(Monitor *mon, const QDict *qdict); +void hmp_balloon(Monitor *mon, const QDict *qdict); +void hmp_block_resize(Monitor *mon, const QDict *qdict); +void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict); +void hmp_migrate_cancel(Monitor *mon, const QDict *qdict); +void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict); +void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict); #endif diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index 6f108f4ce2..cd2985f421 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -2,28 +2,197 @@ * Cortex-A9MPCore internal peripheral emulation. * * Copyright (c) 2009 CodeSourcery. - * Written by Paul Brook + * Copyright (c) 2011 Linaro Limited. + * Written by Paul Brook, Peter Maydell. * * This code is licensed under the GPL. */ -/* 64 external IRQ lines. */ +#include "sysbus.h" + +/* Configuration for arm_gic.c: + * number of external IRQ lines, max number of CPUs, how to ID current CPU + */ #define GIC_NIRQ 96 -#include "mpcore.c" +#define NCPU 4 + +static inline int +gic_get_current_cpu(void) +{ + return cpu_single_env->cpu_index; +} + +#include "arm_gic.c" + +/* A9MP private memory region. */ + +typedef struct a9mp_priv_state { + gic_state gic; + uint32_t scu_control; + uint32_t old_timer_status[8]; + uint32_t num_cpu; + qemu_irq *timer_irq; + MemoryRegion scu_iomem; + MemoryRegion ptimer_iomem; + MemoryRegion container; + DeviceState *mptimer; +} a9mp_priv_state; + +static uint64_t a9_scu_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + a9mp_priv_state *s = (a9mp_priv_state *)opaque; + switch (offset) { + case 0x00: /* Control */ + return s->scu_control; + case 0x04: /* Configuration */ + return (((1 << s->num_cpu) - 1) << 4) | (s->num_cpu - 1); + case 0x08: /* CPU Power Status */ + return 0; + case 0x0c: /* Invalidate All Registers In Secure State */ + return 0; + case 0x40: /* Filtering Start Address Register */ + case 0x44: /* Filtering End Address Register */ + /* RAZ/WI, like an implementation with only one AXI master */ + return 0; + case 0x50: /* SCU Access Control Register */ + case 0x54: /* SCU Non-secure Access Control Register */ + /* unimplemented, fall through */ + default: + return 0; + } +} + +static void a9_scu_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + a9mp_priv_state *s = (a9mp_priv_state *)opaque; + switch (offset) { + case 0x00: /* Control */ + s->scu_control = value & 1; + break; + case 0x4: /* Configuration: RO */ + break; + case 0x0c: /* Invalidate All Registers In Secure State */ + /* no-op as we do not implement caches */ + break; + case 0x40: /* Filtering Start Address Register */ + case 0x44: /* Filtering End Address Register */ + /* RAZ/WI, like an implementation with only one AXI master */ + break; + case 0x8: /* CPU Power Status */ + case 0x50: /* SCU Access Control Register */ + case 0x54: /* SCU Non-secure Access Control Register */ + /* unimplemented, fall through */ + default: + break; + } +} + +static const MemoryRegionOps a9_scu_ops = { + .read = a9_scu_read, + .write = a9_scu_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void a9mpcore_timer_irq_handler(void *opaque, int irq, int level) +{ + a9mp_priv_state *s = (a9mp_priv_state *)opaque; + if (level && !s->old_timer_status[irq]) { + gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1)); + } + s->old_timer_status[irq] = level; +} + +static void a9mp_priv_reset(DeviceState *dev) +{ + a9mp_priv_state *s = FROM_SYSBUSGIC(a9mp_priv_state, sysbus_from_qdev(dev)); + int i; + s->scu_control = 0; + for (i = 0; i < ARRAY_SIZE(s->old_timer_status); i++) { + s->old_timer_status[i] = 0; + } +} + +static int a9mp_priv_init(SysBusDevice *dev) +{ + a9mp_priv_state *s = FROM_SYSBUSGIC(a9mp_priv_state, dev); + SysBusDevice *busdev; + int i; + + if (s->num_cpu > NCPU) { + hw_error("a9mp_priv_init: num-cpu may not be more than %d\n", NCPU); + } + + gic_init(&s->gic, s->num_cpu); + + s->mptimer = qdev_create(NULL, "arm_mptimer"); + qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu); + qdev_init_nofail(s->mptimer); + busdev = sysbus_from_qdev(s->mptimer); + + /* Memory map (addresses are offsets from PERIPHBASE): + * 0x0000-0x00ff -- Snoop Control Unit + * 0x0100-0x01ff -- GIC CPU interface + * 0x0200-0x02ff -- Global Timer + * 0x0300-0x05ff -- nothing + * 0x0600-0x06ff -- private timers and watchdogs + * 0x0700-0x0fff -- nothing + * 0x1000-0x1fff -- GIC Distributor + * + * We should implement the global timer but don't currently do so. + */ + memory_region_init(&s->container, "a9mp-priv-container", 0x2000); + memory_region_init_io(&s->scu_iomem, &a9_scu_ops, s, "a9mp-scu", 0x100); + memory_region_add_subregion(&s->container, 0, &s->scu_iomem); + /* GIC CPU interface */ + memory_region_add_subregion(&s->container, 0x100, &s->gic.cpuiomem[0]); + /* Note that the A9 exposes only the "timer/watchdog for this core" + * memory region, not the "timer/watchdog for core X" ones 11MPcore has. + */ + memory_region_add_subregion(&s->container, 0x600, + sysbus_mmio_get_region(busdev, 0)); + memory_region_add_subregion(&s->container, 0x620, + sysbus_mmio_get_region(busdev, 1)); + memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); + + sysbus_init_mmio(dev, &s->container); + + /* Wire up the interrupt from each watchdog and timer. */ + s->timer_irq = qemu_allocate_irqs(a9mpcore_timer_irq_handler, + s, (s->num_cpu + 1) * 2); + for (i = 0; i < s->num_cpu * 2; i++) { + sysbus_connect_irq(busdev, i, s->timer_irq[i]); + } + return 0; +} + +static const VMStateDescription vmstate_a9mp_priv = { + .name = "a9mpcore_priv", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(scu_control, a9mp_priv_state), + VMSTATE_UINT32_ARRAY(old_timer_status, a9mp_priv_state, 8), + VMSTATE_END_OF_LIST() + } +}; -static SysBusDeviceInfo mpcore_priv_info = { - .init = mpcore_priv_init, +static SysBusDeviceInfo a9mp_priv_info = { + .init = a9mp_priv_init, .qdev.name = "a9mpcore_priv", - .qdev.size = sizeof(mpcore_priv_state), + .qdev.size = sizeof(a9mp_priv_state), + .qdev.vmsd = &vmstate_a9mp_priv, + .qdev.reset = a9mp_priv_reset, .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1), + DEFINE_PROP_UINT32("num-cpu", a9mp_priv_state, num_cpu, 1), DEFINE_PROP_END_OF_LIST(), } }; -static void a9mpcore_register_devices(void) +static void a9mp_register_devices(void) { - sysbus_register_withprop(&mpcore_priv_info); + sysbus_register_withprop(&a9mp_priv_info); } -device_init(a9mpcore_register_devices) +device_init(a9mp_register_devices) diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index 32ecf98309..bc0457e58b 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -7,11 +7,139 @@ * This code is licensed under the GPL. */ +#include "sysbus.h" +#include "qemu-timer.h" + /* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines (+ 32 internal). However my test chip only exposes/reports 32. More importantly Linux falls over if more than 32 are present! */ #define GIC_NIRQ 64 -#include "mpcore.c" + +#define NCPU 4 + +static inline int +gic_get_current_cpu(void) +{ + return cpu_single_env->cpu_index; +} + +#include "arm_gic.c" + +/* MPCore private memory region. */ + +typedef struct mpcore_priv_state { + gic_state gic; + uint32_t scu_control; + int iomemtype; + uint32_t old_timer_status[8]; + uint32_t num_cpu; + qemu_irq *timer_irq; + MemoryRegion iomem; + MemoryRegion container; + DeviceState *mptimer; +} mpcore_priv_state; + +/* Per-CPU private memory mapped IO. */ + +static uint64_t mpcore_scu_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + mpcore_priv_state *s = (mpcore_priv_state *)opaque; + int id; + offset &= 0xff; + /* 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: + hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); + } +} + +static void mpcore_scu_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + mpcore_priv_state *s = (mpcore_priv_state *)opaque; + offset &= 0xff; + /* 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: + hw_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_timer_irq_handler(void *opaque, int irq, int level) +{ + mpcore_priv_state *s = (mpcore_priv_state *)opaque; + if (level && !s->old_timer_status[irq]) { + gic_set_pending_private(&s->gic, irq >> 1, 29 + (irq & 1)); + } + s->old_timer_status[irq] = level; +} + +static void mpcore_priv_map_setup(mpcore_priv_state *s) +{ + int i; + SysBusDevice *busdev = sysbus_from_qdev(s->mptimer); + memory_region_init(&s->container, "mpcode-priv-container", 0x2000); + memory_region_init_io(&s->iomem, &mpcore_scu_ops, s, "mpcore-scu", 0x100); + memory_region_add_subregion(&s->container, 0, &s->iomem); + /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs + * at 0x200, 0x300... + */ + for (i = 0; i < (s->num_cpu + 1); i++) { + target_phys_addr_t offset = 0x100 + (i * 0x100); + memory_region_add_subregion(&s->container, offset, &s->gic.cpuiomem[i]); + } + /* Add the regions for timer and watchdog for "current CPU" and + * for each specific CPU. + */ + s->timer_irq = qemu_allocate_irqs(mpcore_timer_irq_handler, + s, (s->num_cpu + 1) * 2); + for (i = 0; i < (s->num_cpu + 1) * 2; i++) { + /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */ + target_phys_addr_t offset = 0x600 + (i >> 1) * 0x100 + (i & 1) * 0x20; + memory_region_add_subregion(&s->container, offset, + sysbus_mmio_get_region(busdev, i)); + } + memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); + /* Wire up the interrupt from each watchdog and timer. */ + for (i = 0; i < s->num_cpu * 2; i++) { + sysbus_connect_irq(busdev, i, s->timer_irq[i]); + } +} + +static int mpcore_priv_init(SysBusDevice *dev) +{ + mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev); + + gic_init(&s->gic, s->num_cpu); + s->mptimer = qdev_create(NULL, "arm_mptimer"); + qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu); + qdev_init_nofail(s->mptimer); + mpcore_priv_map_setup(s); + sysbus_init_mmio(dev, &s->container); + return 0; +} /* Dummy PIC to route IRQ lines. The baseboard has 4 independent IRQ controllers. The output of these, plus some of the raw input lines diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 1a896fbcb3..9b521195a5 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -103,7 +103,14 @@ typedef struct gic_state int num_cpu; #endif - MemoryRegion iomem; + MemoryRegion iomem; /* Distributor */ +#ifndef NVIC + /* This is just so we can have an opaque pointer which identifies + * both this GIC and which CPU interface we should be accessing. + */ + struct gic_state *backref[NCPU]; + MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */ +#endif } gic_state; /* TODO: Many places that call this routine could be optimized. */ @@ -633,6 +640,54 @@ static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value) } gic_update(s); } + +/* Wrappers to read/write the GIC CPU interface for the current CPU */ +static uint64_t gic_thiscpu_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + gic_state *s = (gic_state *)opaque; + return gic_cpu_read(s, gic_get_current_cpu(), addr & 0xff); +} + +static void gic_thiscpu_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + gic_state *s = (gic_state *)opaque; + gic_cpu_write(s, gic_get_current_cpu(), addr & 0xff, value); +} + +/* Wrappers to read/write the GIC CPU interface for a specific CPU. + * These just decode the opaque pointer into gic_state* + cpu id. + */ +static uint64_t gic_do_cpu_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + gic_state **backref = (gic_state **)opaque; + gic_state *s = *backref; + int id = (backref - s->backref); + return gic_cpu_read(s, id, addr & 0xff); +} + +static void gic_do_cpu_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + gic_state **backref = (gic_state **)opaque; + gic_state *s = *backref; + int id = (backref - s->backref); + gic_cpu_write(s, id, addr & 0xff, value); +} + +static const MemoryRegionOps gic_thiscpu_ops = { + .read = gic_thiscpu_read, + .write = gic_thiscpu_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const MemoryRegionOps gic_cpu_ops = { + .read = gic_do_cpu_read, + .write = gic_do_cpu_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; #endif static void gic_reset(gic_state *s) @@ -752,6 +807,24 @@ static void gic_init(gic_state *s) sysbus_init_irq(&s->busdev, &s->parent_irq[i]); } memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000); +#ifndef NVIC + /* Memory regions for the CPU interfaces (NVIC doesn't have these): + * a region for "CPU interface for this core", then a region for + * "CPU interface for core 0", "for core 1", ... + * NB that the memory region size of 0x100 applies for the 11MPCore + * and also cores following the GIC v1 spec (ie A9). + * GIC v2 defines a larger memory region (0x1000) so this will need + * to be extended when we implement A15. + */ + memory_region_init_io(&s->cpuiomem[0], &gic_thiscpu_ops, s, + "gic_cpu", 0x100); + for (i = 0; i < NUM_CPU(s); i++) { + s->backref[i] = s; + memory_region_init_io(&s->cpuiomem[i+1], &gic_cpu_ops, &s->backref[i], + "gic_cpu", 0x100); + } +#endif + gic_reset(s); register_savevm(NULL, "arm_gic", -1, 2, gic_save, gic_load, s); } diff --git a/hw/arm_mptimer.c b/hw/arm_mptimer.c new file mode 100644 index 0000000000..455a0aa55a --- /dev/null +++ b/hw/arm_mptimer.c @@ -0,0 +1,332 @@ +/* + * 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/>. + */ + +#include "sysbus.h" +#include "qemu-timer.h" + +/* This device implements the per-cpu private timer and watchdog block + * 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; + +typedef struct { + SysBusDevice busdev; + uint32_t num_cpu; + timerblock timerblock[MAX_CPUS * 2]; + MemoryRegion iomem[2]; +} arm_mptimer_state; + +static inline int get_current_cpu(arm_mptimer_state *s) +{ + if (cpu_single_env->cpu_index >= s->num_cpu) { + hw_error("arm_mptimer: num-cpu %d but this cpu is %d!\n", + s->num_cpu, cpu_single_env->cpu_index); + } + return cpu_single_env->cpu_index; +} + +static inline void timerblock_update_irq(timerblock *tb) +{ + qemu_set_irq(tb->irq, tb->status); +} + +/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ +static inline uint32_t timerblock_scale(timerblock *tb) +{ + return (((tb->control >> 8) & 0xff) + 1) * 10; +} + +static void timerblock_reload(timerblock *tb, int restart) +{ + if (tb->count == 0) { + return; + } + if (restart) { + tb->tick = qemu_get_clock_ns(vm_clock); + } + tb->tick += (int64_t)tb->count * timerblock_scale(tb); + qemu_mod_timer(tb->timer, tb->tick); +} + +static void timerblock_tick(void *opaque) +{ + timerblock *tb = (timerblock *)opaque; + tb->status = 1; + if (tb->control & 2) { + tb->count = tb->load; + timerblock_reload(tb, 0); + } else { + tb->count = 0; + } + timerblock_update_irq(tb); +} + +static uint64_t timerblock_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + timerblock *tb = (timerblock *)opaque; + int64_t val; + addr &= 0x1f; + switch (addr) { + case 0: /* Load */ + return tb->load; + case 4: /* Counter. */ + if (((tb->control & 1) == 0) || (tb->count == 0)) { + return 0; + } + /* Slow and ugly, but hopefully won't happen too often. */ + val = tb->tick - qemu_get_clock_ns(vm_clock); + val /= timerblock_scale(tb); + if (val < 0) { + val = 0; + } + return val; + case 8: /* Control. */ + return tb->control; + case 12: /* Interrupt status. */ + return tb->status; + default: + return 0; + } +} + +static void timerblock_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + timerblock *tb = (timerblock *)opaque; + int64_t old; + addr &= 0x1f; + switch (addr) { + case 0: /* Load */ + tb->load = value; + /* Fall through. */ + case 4: /* Counter. */ + if ((tb->control & 1) && tb->count) { + /* Cancel the previous timer. */ + qemu_del_timer(tb->timer); + } + tb->count = value; + if (tb->control & 1) { + timerblock_reload(tb, 1); + } + break; + case 8: /* Control. */ + old = tb->control; + tb->control = value; + if (((old & 1) == 0) && (value & 1)) { + if (tb->count == 0 && (tb->control & 2)) { + tb->count = tb->load; + } + timerblock_reload(tb, 1); + } + break; + case 12: /* Interrupt status. */ + tb->status &= ~value; + timerblock_update_irq(tb); + break; + } +} + +/* Wrapper functions to implement the "read timer/watchdog for + * the current CPU" memory regions. + */ +static uint64_t arm_thistimer_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + arm_mptimer_state *s = (arm_mptimer_state *)opaque; + int id = get_current_cpu(s); + return timerblock_read(&s->timerblock[id * 2], addr, size); +} + +static void arm_thistimer_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + arm_mptimer_state *s = (arm_mptimer_state *)opaque; + int id = get_current_cpu(s); + timerblock_write(&s->timerblock[id * 2], addr, value, size); +} + +static uint64_t arm_thiswdog_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + arm_mptimer_state *s = (arm_mptimer_state *)opaque; + int id = get_current_cpu(s); + return timerblock_read(&s->timerblock[id * 2 + 1], addr, size); +} + +static void arm_thiswdog_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + arm_mptimer_state *s = (arm_mptimer_state *)opaque; + int id = get_current_cpu(s); + timerblock_write(&s->timerblock[id * 2 + 1], addr, value, size); +} + +static const MemoryRegionOps arm_thistimer_ops = { + .read = arm_thistimer_read, + .write = arm_thistimer_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const MemoryRegionOps arm_thiswdog_ops = { + .read = arm_thiswdog_read, + .write = arm_thiswdog_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const MemoryRegionOps timerblock_ops = { + .read = timerblock_read, + .write = timerblock_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void timerblock_reset(timerblock *tb) +{ + tb->count = 0; + tb->load = 0; + tb->control = 0; + tb->status = 0; + tb->tick = 0; +} + +static void arm_mptimer_reset(DeviceState *dev) +{ + arm_mptimer_state *s = + FROM_SYSBUS(arm_mptimer_state, sysbus_from_qdev(dev)); + int i; + /* We reset every timer in the array, not just the ones we're using, + * because vmsave will look at every array element. + */ + for (i = 0; i < ARRAY_SIZE(s->timerblock); i++) { + timerblock_reset(&s->timerblock[i]); + } +} + +static int arm_mptimer_init(SysBusDevice *dev) +{ + arm_mptimer_state *s = FROM_SYSBUS(arm_mptimer_state, 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); + } + /* We implement one timer and one watchdog block per CPU, and + * expose multiple MMIO regions: + * * region 0 is "timer for this core" + * * region 1 is "watchdog for this core" + * * region 2 is "timer for core 0" + * * region 3 is "watchdog for core 0" + * * region 4 is "timer for core 1" + * * region 5 is "watchdog for core 1" + * and so on. + * The outgoing interrupt lines are + * * timer for core 0 + * * watchdog for core 0 + * * timer for core 1 + * * watchdog for core 1 + * and so on. + */ + memory_region_init_io(&s->iomem[0], &arm_thistimer_ops, s, + "arm_mptimer_timer", 0x20); + sysbus_init_mmio(dev, &s->iomem[0]); + memory_region_init_io(&s->iomem[1], &arm_thiswdog_ops, s, + "arm_mptimer_wdog", 0x20); + sysbus_init_mmio(dev, &s->iomem[1]); + for (i = 0; i < (s->num_cpu * 2); i++) { + timerblock *tb = &s->timerblock[i]; + tb->timer = qemu_new_timer_ns(vm_clock, timerblock_tick, tb); + sysbus_init_irq(dev, &tb->irq); + memory_region_init_io(&tb->iomem, &timerblock_ops, tb, + "arm_mptimer_timerblock", 0x20); + sysbus_init_mmio(dev, &tb->iomem); + } + + return 0; +} + +static const VMStateDescription vmstate_timerblock = { + .name = "arm_mptimer_timerblock", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(count, timerblock), + VMSTATE_UINT32(load, timerblock), + VMSTATE_UINT32(control, timerblock), + VMSTATE_UINT32(status, timerblock), + VMSTATE_INT64(tick, timerblock), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_arm_mptimer = { + .name = "arm_mptimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(timerblock, arm_mptimer_state, (MAX_CPUS * 2), + 1, vmstate_timerblock, timerblock), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo arm_mptimer_info = { + .init = arm_mptimer_init, + .qdev.name = "arm_mptimer", + .qdev.size = sizeof(arm_mptimer_state), + .qdev.vmsd = &vmstate_arm_mptimer, + .qdev.reset = arm_mptimer_reset, + .qdev.no_user = 1, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("num-cpu", arm_mptimer_state, num_cpu, 0), + DEFINE_PROP_END_OF_LIST() + } +}; + +static void arm_mptimer_register_devices(void) +{ + sysbus_register_withprop(&arm_mptimer_info); +} + +device_init(arm_mptimer_register_devices) diff --git a/hw/arm_timer.c b/hw/arm_timer.c index 518bad20ae..0a5b9d2cd3 100644 --- a/hw/arm_timer.c +++ b/hw/arm_timer.c @@ -170,9 +170,9 @@ static arm_timer_state *arm_timer_init(uint32_t freq) } /* ARM PrimeCell SP804 dual timer module. - Docs for this device don't seem to be publicly available. This - implementation is based on guesswork, the linux kernel sources and the - Integrator/CP timer modules. */ + * Docs at + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0271d/index.html +*/ typedef struct { SysBusDevice busdev; @@ -182,6 +182,13 @@ typedef struct { qemu_irq irq; } sp804_state; +static const uint8_t sp804_ids[] = { + /* Timer ID */ + 0x04, 0x18, 0x14, 0, + /* PrimeCell ID */ + 0xd, 0xf0, 0x05, 0xb1 +}; + /* Merge the IRQs from the two component devices. */ static void sp804_set_irq(void *opaque, int irq, int level) { @@ -196,12 +203,27 @@ static uint64_t sp804_read(void *opaque, target_phys_addr_t offset, { sp804_state *s = (sp804_state *)opaque; - /* ??? Don't know the PrimeCell ID for this device. */ if (offset < 0x20) { return arm_timer_read(s->timer[0], offset); - } else { + } + if (offset < 0x40) { return arm_timer_read(s->timer[1], offset - 0x20); } + + /* TimerPeriphID */ + if (offset >= 0xfe0 && offset <= 0xffc) { + return sp804_ids[(offset - 0xfe0) >> 2]; + } + + switch (offset) { + /* Integration Test control registers, which we won't support */ + case 0xf00: /* TimerITCR */ + case 0xf04: /* TimerITOP (strictly write only but..) */ + return 0; + } + + hw_error("%s: Bad offset %x\n", __func__, (int)offset); + return 0; } static void sp804_write(void *opaque, target_phys_addr_t offset, @@ -211,9 +233,16 @@ static void sp804_write(void *opaque, target_phys_addr_t offset, if (offset < 0x20) { arm_timer_write(s->timer[0], offset, value); - } else { + return; + } + + if (offset < 0x40) { arm_timer_write(s->timer[1], offset - 0x20, value); + return; } + + /* Technically we could be writing to the Test Registers, but not likely */ + hw_error("%s: Bad offset %x\n", __func__, (int)offset); } static const MemoryRegionOps sp804_ops = { diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c index 092301b541..2d2ebce852 100644 --- a/hw/ccid-card-emulated.c +++ b/hw/ccid-card-emulated.c @@ -120,6 +120,7 @@ struct EmulatedState { uint8_t atr_length; QSIMPLEQ_HEAD(event_list, EmulEvent) event_list; QemuMutex event_list_mutex; + QemuThread event_thread_id; VReader *reader; QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list; QemuMutex vreader_mutex; /* and guest_apdu_list mutex */ @@ -127,8 +128,7 @@ struct EmulatedState { QemuCond handle_apdu_cond; int pipe[2]; int quit_apdu_thread; - QemuMutex apdu_thread_quit_mutex; - QemuCond apdu_thread_quit_cond; + QemuThread apdu_thread_id; }; static void emulated_apdu_from_guest(CCIDCardState *base, @@ -271,9 +271,6 @@ static void *handle_apdu_thread(void* arg) } qemu_mutex_unlock(&card->vreader_mutex); } - qemu_mutex_lock(&card->apdu_thread_quit_mutex); - qemu_cond_signal(&card->apdu_thread_quit_cond); - qemu_mutex_unlock(&card->apdu_thread_quit_mutex); return NULL; } @@ -489,7 +486,6 @@ static uint32_t parse_enumeration(char *str, static int emulated_initfn(CCIDCardState *base) { EmulatedState *card = DO_UPCAST(EmulatedState, base, base); - QemuThread thread_id; VCardEmulError ret; EnumTable *ptable; @@ -541,8 +537,10 @@ static int emulated_initfn(CCIDCardState *base) printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME); return -1; } - qemu_thread_create(&thread_id, event_thread, card); - qemu_thread_create(&thread_id, handle_apdu_thread, card); + qemu_thread_create(&card->event_thread_id, event_thread, card, + QEMU_THREAD_JOINABLE); + qemu_thread_create(&card->apdu_thread_id, handle_apdu_thread, card, + QEMU_THREAD_JOINABLE); return 0; } @@ -552,15 +550,14 @@ static int emulated_exitfn(CCIDCardState *base) VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); vevent_queue_vevent(vevent); /* stop vevent thread */ - qemu_mutex_lock(&card->apdu_thread_quit_mutex); + qemu_thread_join(&card->event_thread_id); + card->quit_apdu_thread = 1; /* stop handle_apdu thread */ qemu_cond_signal(&card->handle_apdu_cond); - qemu_cond_wait(&card->apdu_thread_quit_cond, - &card->apdu_thread_quit_mutex); - /* handle_apdu thread stopped, can destroy all of it's mutexes */ + qemu_thread_join(&card->apdu_thread_id); + + /* threads exited, can destroy all condvars/mutexes */ qemu_cond_destroy(&card->handle_apdu_cond); - qemu_cond_destroy(&card->apdu_thread_quit_cond); - qemu_mutex_destroy(&card->apdu_thread_quit_mutex); qemu_mutex_destroy(&card->handle_apdu_mutex); qemu_mutex_destroy(&card->vreader_mutex); qemu_mutex_destroy(&card->event_list_mutex); diff --git a/hw/e1000.c b/hw/e1000.c index 986ed9cf79..a29c944df4 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -507,7 +507,7 @@ txdesc_writeback(E1000State *s, dma_addr_t base, struct e1000_tx_desc *dp) ~(E1000_TXD_STAT_EC | E1000_TXD_STAT_LC | E1000_TXD_STAT_TU); dp->upper.data = cpu_to_le32(txd_upper); pci_dma_write(&s->dev, base + ((char *)&dp->upper - (char *)dp), - (void *)&dp->upper, sizeof(dp->upper)); + &dp->upper, sizeof(dp->upper)); return E1000_ICR_TXDW; } @@ -534,7 +534,7 @@ start_xmit(E1000State *s) while (s->mac_reg[TDH] != s->mac_reg[TDT]) { base = tx_desc_base(s) + sizeof(struct e1000_tx_desc) * s->mac_reg[TDH]; - pci_dma_read(&s->dev, base, (void *)&desc, sizeof(desc)); + pci_dma_read(&s->dev, base, &desc, sizeof(desc)); DBGOUT(TX, "index %d: %p : %x %x\n", s->mac_reg[TDH], (void *)(intptr_t)desc.buffer_addr, desc.lower.data, @@ -714,7 +714,7 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) desc_size = s->rxbuf_size; } base = rx_desc_base(s) + sizeof(desc) * s->mac_reg[RDH]; - pci_dma_read(&s->dev, base, (void *)&desc, sizeof(desc)); + pci_dma_read(&s->dev, base, &desc, sizeof(desc)); desc.special = vlan_special; desc.status |= (vlan_status | E1000_RXD_STAT_DD); if (desc.buffer_addr) { @@ -724,8 +724,7 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) copy_size = s->rxbuf_size; } pci_dma_write(&s->dev, le64_to_cpu(desc.buffer_addr), - (void *)(buf + desc_offset + vlan_offset), - copy_size); + buf + desc_offset + vlan_offset, copy_size); } desc_offset += desc_size; desc.length = cpu_to_le16(desc_size); @@ -739,7 +738,7 @@ e1000_receive(VLANClientState *nc, const uint8_t *buf, size_t size) } else { // as per intel docs; skip descriptors with null buf addr DBGOUT(RX, "Null RX descriptor!!\n"); } - pci_dma_write(&s->dev, base, (void *)&desc, sizeof(desc)); + pci_dma_write(&s->dev, base, &desc, sizeof(desc)); if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN]) s->mac_reg[RDH] = 0; diff --git a/hw/eepro100.c b/hw/eepro100.c index e430f56b29..6a162f607f 100644 --- a/hw/eepro100.c +++ b/hw/eepro100.c @@ -713,8 +713,7 @@ static void dump_statistics(EEPRO100State * s) * values which really matter. * Number of data should check configuration!!! */ - pci_dma_write(&s->dev, s->statsaddr, - (uint8_t *) &s->statistics, s->stats_size); + pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size); stl_le_pci_dma(&s->dev, s->statsaddr + 0, s->statistics.tx_good_frames); stl_le_pci_dma(&s->dev, s->statsaddr + 36, @@ -732,7 +731,7 @@ static void dump_statistics(EEPRO100State * s) static void read_cb(EEPRO100State *s) { - pci_dma_read(&s->dev, s->cb_address, (uint8_t *) &s->tx, sizeof(s->tx)); + pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx)); s->tx.status = le16_to_cpu(s->tx.status); s->tx.command = le16_to_cpu(s->tx.command); s->tx.link = le32_to_cpu(s->tx.link); @@ -1715,7 +1714,7 @@ static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size /* !!! */ eepro100_rx_t rx; pci_dma_read(&s->dev, s->ru_base + s->ru_offset, - (uint8_t *) &rx, sizeof(eepro100_rx_t)); + &rx, sizeof(eepro100_rx_t)); uint16_t rfd_command = le16_to_cpu(rx.command); uint16_t rfd_size = le16_to_cpu(rx.size); @@ -27,7 +27,13 @@ typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf, typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, int64_t pos, int size); -/* Close a file and return an error code */ +/* Close a file + * + * Return negative error number on error, 0 or positive value on success. + * + * The meaning of return value on success depends on the specific back-end being + * used. + */ typedef int (QEMUFileCloseFunc)(void *opaque); /* Called to determine if the file has exceeded it's bandwidth allocation. The diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 5078c0b565..cb3de6537b 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -71,7 +71,7 @@ static int bmdma_prepare_buf(IDEDMA *dma, int is_write) if (bm->cur_prd_last || (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) return s->io_buffer_size != 0; - pci_dma_read(&bm->pci_dev->dev, bm->cur_addr, (uint8_t *)&prd, 8); + pci_dma_read(&bm->pci_dev->dev, bm->cur_addr, &prd, 8); bm->cur_addr += 8; prd.addr = le32_to_cpu(prd.addr); prd.size = le32_to_cpu(prd.size); @@ -113,7 +113,7 @@ static int bmdma_rw_buf(IDEDMA *dma, int is_write) if (bm->cur_prd_last || (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) return 0; - pci_dma_read(&bm->pci_dev->dev, bm->cur_addr, (uint8_t *)&prd, 8); + pci_dma_read(&bm->pci_dev->dev, bm->cur_addr, &prd, 8); bm->cur_addr += 8; prd.addr = le32_to_cpu(prd.addr); prd.size = le32_to_cpu(prd.size); diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index fcc27d726f..0d3a1016df 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -392,7 +392,7 @@ static inline uint32_t read_dword(LSIState *s, uint32_t addr) { uint32_t buf; - pci_dma_read(&s->dev, addr, (uint8_t *)&buf, 4); + pci_dma_read(&s->dev, addr, &buf, 4); return cpu_to_le32(buf); } @@ -1079,7 +1079,7 @@ again: /* 32-bit Table indirect */ offset = sxt24(addr); - pci_dma_read(&s->dev, s->dsa + offset, (uint8_t *)buf, 8); + pci_dma_read(&s->dev, s->dsa + offset, buf, 8); /* byte count is stored in bits 0:23 only */ s->dbc = cpu_to_le32(buf[0]) & 0xffffff; s->rbc = s->dbc; diff --git a/hw/mpcore.c b/hw/mpcore.c deleted file mode 100644 index 4357d12217..0000000000 --- a/hw/mpcore.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * ARM MPCore internal peripheral emulation (common code). - * - * Copyright (c) 2006-2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "sysbus.h" -#include "qemu-timer.h" - -#define NCPU 4 - -static inline int -gic_get_current_cpu(void) -{ - return cpu_single_env->cpu_index; -} - -#include "arm_gic.c" - -/* MPCore private memory region. */ - -typedef struct { - uint32_t count; - uint32_t load; - uint32_t control; - uint32_t status; - uint32_t old_status; - int64_t tick; - QEMUTimer *timer; - struct mpcore_priv_state *mpcore; - int id; /* Encodes both timer/watchdog and CPU. */ -} mpcore_timer_state; - -typedef struct mpcore_priv_state { - gic_state gic; - uint32_t scu_control; - int iomemtype; - mpcore_timer_state timer[8]; - uint32_t num_cpu; - MemoryRegion iomem; - MemoryRegion container; -} mpcore_priv_state; - -/* Per-CPU Timers. */ - -static inline void mpcore_timer_update_irq(mpcore_timer_state *s) -{ - if (s->status & ~s->old_status) { - gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 29 + (s->id & 1)); - } - s->old_status = s->status; -} - -/* Return conversion factor from mpcore timer ticks to qemu timer ticks. */ -static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s) -{ - return (((s->control >> 8) & 0xff) + 1) * 10; -} - -static void mpcore_timer_reload(mpcore_timer_state *s, int restart) -{ - if (s->count == 0) - return; - if (restart) - s->tick = qemu_get_clock_ns(vm_clock); - s->tick += (int64_t)s->count * mpcore_timer_scale(s); - qemu_mod_timer(s->timer, s->tick); -} - -static void mpcore_timer_tick(void *opaque) -{ - mpcore_timer_state *s = (mpcore_timer_state *)opaque; - s->status = 1; - if (s->control & 2) { - s->count = s->load; - mpcore_timer_reload(s, 0); - } else { - s->count = 0; - } - mpcore_timer_update_irq(s); -} - -static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset) -{ - int64_t val; - switch (offset) { - case 0: /* Load */ - return s->load; - /* Fall through. */ - case 4: /* Counter. */ - if (((s->control & 1) == 0) || (s->count == 0)) - return 0; - /* Slow and ugly, but hopefully won't happen too often. */ - val = s->tick - qemu_get_clock_ns(vm_clock); - val /= mpcore_timer_scale(s); - if (val < 0) - val = 0; - return val; - case 8: /* Control. */ - return s->control; - case 12: /* Interrupt status. */ - return s->status; - default: - return 0; - } -} - -static void mpcore_timer_write(mpcore_timer_state *s, int offset, - uint32_t value) -{ - int64_t old; - switch (offset) { - case 0: /* Load */ - s->load = value; - /* Fall through. */ - case 4: /* Counter. */ - if ((s->control & 1) && s->count) { - /* Cancel the previous timer. */ - qemu_del_timer(s->timer); - } - s->count = value; - if (s->control & 1) { - mpcore_timer_reload(s, 1); - } - break; - case 8: /* Control. */ - old = s->control; - s->control = value; - if (((old & 1) == 0) && (value & 1)) { - if (s->count == 0 && (s->control & 2)) - s->count = s->load; - mpcore_timer_reload(s, 1); - } - break; - case 12: /* Interrupt status. */ - s->status &= ~value; - mpcore_timer_update_irq(s); - break; - } -} - -static void mpcore_timer_init(mpcore_priv_state *mpcore, - mpcore_timer_state *s, int id) -{ - s->id = id; - s->mpcore = mpcore; - s->timer = qemu_new_timer_ns(vm_clock, mpcore_timer_tick, s); -} - - -/* Per-CPU private memory mapped IO. */ - -static uint64_t mpcore_priv_read(void *opaque, target_phys_addr_t offset, - unsigned size) -{ - mpcore_priv_state *s = (mpcore_priv_state *)opaque; - int id; - offset &= 0xfff; - if (offset < 0x100) { - /* 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: - goto bad_reg; - } - } else if (offset < 0x600) { - /* Interrupt controller. */ - if (offset < 0x200) { - id = gic_get_current_cpu(); - } else { - id = (offset - 0x200) >> 8; - if (id >= s->num_cpu) { - return 0; - } - } - return gic_cpu_read(&s->gic, id, offset & 0xff); - } else if (offset < 0xb00) { - /* Timers. */ - if (offset < 0x700) { - id = gic_get_current_cpu(); - } else { - id = (offset - 0x700) >> 8; - if (id >= s->num_cpu) { - return 0; - } - } - id <<= 1; - if (offset & 0x20) - id++; - return mpcore_timer_read(&s->timer[id], offset & 0xf); - } -bad_reg: - hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); - return 0; -} - -static void mpcore_priv_write(void *opaque, target_phys_addr_t offset, - uint64_t value, unsigned size) -{ - mpcore_priv_state *s = (mpcore_priv_state *)opaque; - int id; - offset &= 0xfff; - if (offset < 0x100) { - /* 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: - goto bad_reg; - } - } else if (offset < 0x600) { - /* Interrupt controller. */ - if (offset < 0x200) { - id = gic_get_current_cpu(); - } else { - id = (offset - 0x200) >> 8; - } - if (id < s->num_cpu) { - gic_cpu_write(&s->gic, id, offset & 0xff, value); - } - } else if (offset < 0xb00) { - /* Timers. */ - if (offset < 0x700) { - id = gic_get_current_cpu(); - } else { - id = (offset - 0x700) >> 8; - } - if (id < s->num_cpu) { - id <<= 1; - if (offset & 0x20) - id++; - mpcore_timer_write(&s->timer[id], offset & 0xf, value); - } - return; - } - return; -bad_reg: - hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset); -} - -static const MemoryRegionOps mpcore_priv_ops = { - .read = mpcore_priv_read, - .write = mpcore_priv_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void mpcore_priv_map_setup(mpcore_priv_state *s) -{ - memory_region_init(&s->container, "mpcode-priv-container", 0x2000); - memory_region_init_io(&s->iomem, &mpcore_priv_ops, s, "mpcode-priv", - 0x1000); - memory_region_add_subregion(&s->container, 0, &s->iomem); - memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); -} - -static int mpcore_priv_init(SysBusDevice *dev) -{ - mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev); - int i; - - gic_init(&s->gic, s->num_cpu); - mpcore_priv_map_setup(s); - sysbus_init_mmio(dev, &s->container); - for (i = 0; i < s->num_cpu * 2; i++) { - mpcore_timer_init(s, &s->timer[i], i); - } - return 0; -} diff --git a/hw/realview_gic.c b/hw/realview_gic.c index 479f939553..8c4d509ee7 100644 --- a/hw/realview_gic.c +++ b/hw/realview_gic.c @@ -23,36 +23,13 @@ gic_get_current_cpu(void) typedef struct { gic_state gic; - MemoryRegion iomem; MemoryRegion container; } RealViewGICState; -static uint64_t realview_gic_cpu_read(void *opaque, target_phys_addr_t offset, - unsigned size) -{ - gic_state *s = (gic_state *)opaque; - return gic_cpu_read(s, gic_get_current_cpu(), offset); -} - -static void realview_gic_cpu_write(void *opaque, target_phys_addr_t offset, - uint64_t value, unsigned size) -{ - gic_state *s = (gic_state *)opaque; - gic_cpu_write(s, gic_get_current_cpu(), offset, value); -} - -static const MemoryRegionOps realview_gic_cpu_ops = { - .read = realview_gic_cpu_read, - .write = realview_gic_cpu_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - static void realview_gic_map_setup(RealViewGICState *s) { memory_region_init(&s->container, "realview-gic-container", 0x2000); - memory_region_init_io(&s->iomem, &realview_gic_cpu_ops, &s->gic, - "realview-gic", 0x1000); - memory_region_add_subregion(&s->container, 0, &s->iomem); + memory_region_add_subregion(&s->container, 0, &s->gic.cpuiomem[0]); memory_region_add_subregion(&s->container, 0x1000, &s->gic.iomem); } diff --git a/hw/rtl8139.c b/hw/rtl8139.c index 8aace9a4ec..0ae9f5774b 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -991,13 +991,13 @@ static ssize_t rtl8139_do_receive(VLANClientState *nc, const uint8_t *buf, size_ uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI; - pci_dma_read(&s->dev, cplus_rx_ring_desc, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_rx_ring_desc, &val, 4); rxdw0 = le32_to_cpu(val); - pci_dma_read(&s->dev, cplus_rx_ring_desc+4, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_rx_ring_desc+4, &val, 4); rxdw1 = le32_to_cpu(val); - pci_dma_read(&s->dev, cplus_rx_ring_desc+8, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_rx_ring_desc+8, &val, 4); rxbufLO = le32_to_cpu(val); - pci_dma_read(&s->dev, cplus_rx_ring_desc+12, (uint8_t *)&val, 4); + pci_dma_read(&s->dev, cplus_rx_ring_desc+12, &val, 4); rxbufHI = le32_to_cpu(val); DPRINTF("+++ C+ mode RX descriptor %d %08x %08x %08x %08x\n", diff --git a/hw/syborg.c b/hw/syborg.c deleted file mode 100644 index 248de54c4e..0000000000 --- a/hw/syborg.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Syborg (Symbian Virtual Platform) reference board - * - * Copyright (c) 2009 CodeSourcery - * - * 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 "sysbus.h" -#include "boards.h" -#include "arm-misc.h" -#include "net.h" -#include "exec-memory.h" - -static struct arm_boot_info syborg_binfo; - -static void syborg_init(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, const char *kernel_cmdline, - const char *initrd_filename, const char *cpu_model) -{ - CPUState *env; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *ram = g_new(MemoryRegion, 1); - qemu_irq *cpu_pic; - qemu_irq pic[64]; - DeviceState *dev; - int i; - - if (!cpu_model) - cpu_model = "cortex-a8"; - env = cpu_init(cpu_model); - if (!env) { - fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); - } - - /* RAM at address zero. */ - memory_region_init_ram(ram, NULL, "syborg.ram", ram_size); - memory_region_add_subregion(sysmem, 0, ram); - - cpu_pic = arm_pic_init_cpu(env); - dev = sysbus_create_simple("syborg,interrupt", 0xC0000000, - cpu_pic[ARM_PIC_CPU_IRQ]); - for (i = 0; i < 64; i++) { - pic[i] = qdev_get_gpio_in(dev, i); - } - - sysbus_create_simple("syborg,rtc", 0xC0001000, NULL); - - dev = qdev_create(NULL, "syborg,timer"); - qdev_prop_set_uint32(dev, "frequency", 1000000); - qdev_init_nofail(dev); - sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xC0002000); - sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[1]); - - sysbus_create_simple("syborg,keyboard", 0xC0003000, pic[2]); - sysbus_create_simple("syborg,pointer", 0xC0004000, pic[3]); - sysbus_create_simple("syborg,framebuffer", 0xC0005000, pic[4]); - sysbus_create_simple("syborg,serial", 0xC0006000, pic[5]); - sysbus_create_simple("syborg,serial", 0xC0007000, pic[6]); - sysbus_create_simple("syborg,serial", 0xC0008000, pic[7]); - sysbus_create_simple("syborg,serial", 0xC0009000, pic[8]); - - if (nd_table[0].vlan || nd_table[0].netdev) { - DeviceState *dev; - SysBusDevice *s; - - qemu_check_nic_model(&nd_table[0], "virtio"); - dev = qdev_create(NULL, "syborg,virtio-net"); - qdev_set_nic_properties(dev, &nd_table[0]); - qdev_init_nofail(dev); - s = sysbus_from_qdev(dev); - sysbus_mmio_map(s, 0, 0xc000c000); - sysbus_connect_irq(s, 0, pic[9]); - } - - syborg_binfo.ram_size = ram_size; - syborg_binfo.kernel_filename = kernel_filename; - syborg_binfo.kernel_cmdline = kernel_cmdline; - syborg_binfo.initrd_filename = initrd_filename; - syborg_binfo.board_id = 0; - arm_load_kernel(env, &syborg_binfo); -} - -static QEMUMachine syborg_machine = { - .name = "syborg", - .desc = "Syborg (Symbian Virtual Platform)", - .init = syborg_init, -}; - -static void syborg_machine_init(void) -{ - qemu_register_machine(&syborg_machine); -} - -machine_init(syborg_machine_init); diff --git a/hw/syborg.h b/hw/syborg.h deleted file mode 100644 index b82ce4a502..0000000000 --- a/hw/syborg.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _SYBORG_H -#define _SYBORG_H - -#define SYBORG_ID_PLATFORM 0xc51d1000 -#define SYBORG_ID_INT 0xc51d0000 -#define SYBORG_ID_SERIAL 0xc51d0001 -#define SYBORG_ID_KEYBOARD 0xc51d0002 -#define SYBORG_ID_TIMER 0xc51d0003 -#define SYBORG_ID_RTC 0xc51d0004 -#define SYBORG_ID_MOUSE 0xc51d0005 -#define SYBORG_ID_TOUCHSCREEN 0xc51d0006 -#define SYBORG_ID_FRAMEBUFFER 0xc51d0007 -#define SYBORG_ID_HOSTFS 0xc51d0008 -#define SYBORG_ID_SNAPSHOT 0xc51d0009 -#define SYBORG_ID_VIRTIO 0xc51d000a -#define SYBORG_ID_NAND 0xc51d000b - -#endif diff --git a/hw/syborg_fb.c b/hw/syborg_fb.c deleted file mode 100644 index b87d7e6d10..0000000000 --- a/hw/syborg_fb.c +++ /dev/null @@ -1,554 +0,0 @@ -/* - * Syborg Framebuffer - * - * Copyright (c) 2009 CodeSourcery - * - * 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 "sysbus.h" -#include "console.h" -#include "syborg.h" -#include "framebuffer.h" - -//#define DEBUG_SYBORG_FB - -#ifdef DEBUG_SYBORG_FB -#define DPRINTF(fmt, ...) \ -do { printf("syborg_fb: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_fb: error: " fmt , ## __VA_ARGS__); \ - exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_fb: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -enum { - FB_ID = 0, - FB_BASE = 1, - FB_HEIGHT = 2, - FB_WIDTH = 3, - FB_ORIENTATION = 4, - FB_BLANK = 5, - FB_INT_MASK = 6, - FB_INTERRUPT_CAUSE = 7, - FB_BPP = 8, - FB_COLOR_ORDER = 9, - FB_BYTE_ORDER = 10, - FB_PIXEL_ORDER = 11, - FB_ROW_PITCH = 12, - FB_ENABLED = 13, - FB_PALETTE_START = 0x400 >> 2, - FB_PALETTE_END = FB_PALETTE_START+256-1, -}; - -#define FB_INT_VSYNC (1U << 0) -#define FB_INT_BASE_UPDATE_DONE (1U << 1) - -typedef struct { - SysBusDevice busdev; - MemoryRegion iomem; - DisplayState *ds; - /*QEMUConsole *console;*/ - uint32_t need_update : 1; - uint32_t need_int : 1; - uint32_t enabled : 1; - uint32_t int_status; - uint32_t int_enable; - qemu_irq irq; - - uint32_t base; - uint32_t pitch; - uint32_t rows; - uint32_t cols; - int blank; - int bpp; - int rgb; /* 0 = BGR, 1 = RGB */ - int endian; /* 0 = Little, 1 = Big */ - uint32_t raw_palette[256]; - uint32_t palette[256]; -} SyborgFBState; - -enum { - BPP_SRC_1, - BPP_SRC_2, - BPP_SRC_4, - BPP_SRC_8, - BPP_SRC_16, - BPP_SRC_32, - /* TODO: Implement these. */ - BPP_SRC_15 = -1, - BPP_SRC_24 = -2 -}; - -#include "pixel_ops.h" - -#define BITS 8 -#include "pl110_template.h" -#define BITS 15 -#include "pl110_template.h" -#define BITS 16 -#include "pl110_template.h" -#define BITS 24 -#include "pl110_template.h" -#define BITS 32 -#include "pl110_template.h" - -/* Update interrupts. */ -static void syborg_fb_update(SyborgFBState *s) -{ - if ((s->int_status & s->int_enable) != 0) { - DPRINTF("Raise IRQ\n"); - qemu_irq_raise(s->irq); - } else { - DPRINTF("Lower IRQ\n"); - qemu_irq_lower(s->irq); - } -} - -static int syborg_fb_enabled(const SyborgFBState *s) -{ - return s->enabled; -} - -static void syborg_fb_update_palette(SyborgFBState *s) -{ - int n, i; - uint32_t raw; - unsigned int r, g, b; - - switch (s->bpp) { - case BPP_SRC_1: n = 2; break; - case BPP_SRC_2: n = 4; break; - case BPP_SRC_4: n = 16; break; - case BPP_SRC_8: n = 256; break; - default: return; - } - - for (i = 0; i < n; i++) { - raw = s->raw_palette[i]; - r = (raw >> 16) & 0xff; - g = (raw >> 8) & 0xff; - b = raw & 0xff; - switch (ds_get_bits_per_pixel(s->ds)) { - case 8: - s->palette[i] = rgb_to_pixel8(r, g, b); - break; - case 15: - s->palette[i] = rgb_to_pixel15(r, g, b); - break; - case 16: - s->palette[i] = rgb_to_pixel16(r, g, b); - break; - case 24: - case 32: - s->palette[i] = rgb_to_pixel32(r, g, b); - break; - default: - abort(); - } - } - -} - -static void syborg_fb_update_display(void *opaque) -{ - SyborgFBState *s = (SyborgFBState *)opaque; - drawfn* fntable; - drawfn fn; - int dest_width; - int src_width; - int bpp_offset; - int first; - int last; - - if (!syborg_fb_enabled(s)) - return; - - switch (ds_get_bits_per_pixel(s->ds)) { - case 0: - return; - case 8: - fntable = pl110_draw_fn_8; - dest_width = 1; - break; - case 15: - fntable = pl110_draw_fn_15; - dest_width = 2; - break; - case 16: - fntable = pl110_draw_fn_16; - dest_width = 2; - break; - case 24: - fntable = pl110_draw_fn_24; - dest_width = 3; - break; - case 32: - fntable = pl110_draw_fn_32; - dest_width = 4; - break; - default: - fprintf(stderr, "syborg_fb: Bad color depth\n"); - exit(1); - } - - if (s->need_int) { - s->int_status |= FB_INT_BASE_UPDATE_DONE; - syborg_fb_update(s); - s->need_int = 0; - } - - if (s->rgb) { - bpp_offset = 24; - } else { - bpp_offset = 0; - } - if (s->endian) { - bpp_offset += 8; - } - /* Our bpp constants mostly match the PL110/PL111 but - * not for the 16 bit case - */ - switch (s->bpp) { - case BPP_SRC_16: - bpp_offset += 6; - break; - default: - bpp_offset += s->bpp; - } - fn = fntable[bpp_offset]; - - if (s->pitch) { - src_width = s->pitch; - } else { - src_width = s->cols; - switch (s->bpp) { - case BPP_SRC_1: - src_width >>= 3; - break; - case BPP_SRC_2: - src_width >>= 2; - break; - case BPP_SRC_4: - src_width >>= 1; - break; - case BPP_SRC_8: - break; - case BPP_SRC_15: - case BPP_SRC_16: - src_width <<= 1; - break; - case BPP_SRC_24: - src_width *= 3; - break; - case BPP_SRC_32: - src_width <<= 2; - break; - } - } - dest_width *= s->cols; - first = 0; - /* TODO: Implement blanking. */ - if (!s->blank) { - if (s->need_update && s->bpp <= BPP_SRC_8) { - syborg_fb_update_palette(s); - } - framebuffer_update_display(s->ds, - s->base, s->cols, s->rows, - src_width, dest_width, 0, - s->need_update, - fn, s->palette, - &first, &last); - if (first >= 0) { - dpy_update(s->ds, 0, first, s->cols, last - first + 1); - } - - s->int_status |= FB_INT_VSYNC; - syborg_fb_update(s); - } - - s->need_update = 0; -} - -static void syborg_fb_invalidate_display(void * opaque) -{ - SyborgFBState *s = (SyborgFBState *)opaque; - s->need_update = 1; -} - -static uint64_t syborg_fb_read(void *opaque, target_phys_addr_t offset, - unsigned size) -{ - SyborgFBState *s = opaque; - - DPRINTF("read reg %d\n", (int)offset); - offset &= 0xfff; - switch (offset >> 2) { - case FB_ID: - return SYBORG_ID_FRAMEBUFFER; - - case FB_BASE: - return s->base; - - case FB_HEIGHT: - return s->rows; - - case FB_WIDTH: - return s->cols; - - case FB_ORIENTATION: - return 0; - - case FB_BLANK: - return s->blank; - - case FB_INT_MASK: - return s->int_enable; - - case FB_INTERRUPT_CAUSE: - return s->int_status; - - case FB_BPP: - switch (s->bpp) { - case BPP_SRC_1: return 1; - case BPP_SRC_2: return 2; - case BPP_SRC_4: return 4; - case BPP_SRC_8: return 8; - case BPP_SRC_15: return 15; - case BPP_SRC_16: return 16; - case BPP_SRC_24: return 24; - case BPP_SRC_32: return 32; - default: return 0; - } - - case FB_COLOR_ORDER: - return s->rgb; - - case FB_BYTE_ORDER: - return s->endian; - - case FB_PIXEL_ORDER: - return 0; - - case FB_ROW_PITCH: - return s->pitch; - - case FB_ENABLED: - return s->enabled; - - default: - if ((offset >> 2) >= FB_PALETTE_START - && (offset >> 2) <= FB_PALETTE_END) { - return s->raw_palette[(offset >> 2) - FB_PALETTE_START]; - } else { - cpu_abort (cpu_single_env, "syborg_fb_read: Bad offset %x\n", - (int)offset); - } - return 0; - } -} - -static void syborg_fb_write(void *opaque, target_phys_addr_t offset, - uint64_t val, unsigned size) -{ - SyborgFBState *s = opaque; - - DPRINTF("write reg %d = %d\n", (int)offset, val); - s->need_update = 1; - offset &= 0xfff; - switch (offset >> 2) { - case FB_BASE: - s->base = val; - s->need_int = 1; - s->need_update = 1; - syborg_fb_update(s); - break; - - case FB_HEIGHT: - s->rows = val; - break; - - case FB_WIDTH: - s->cols = val; - break; - - case FB_ORIENTATION: - /* TODO: Implement rotation. */ - break; - - case FB_BLANK: - s->blank = val & 1; - break; - - case FB_INT_MASK: - s->int_enable = val; - syborg_fb_update(s); - break; - - case FB_INTERRUPT_CAUSE: - s->int_status &= ~val; - syborg_fb_update(s); - break; - - case FB_BPP: - switch (val) { - case 1: val = BPP_SRC_1; break; - case 2: val = BPP_SRC_2; break; - case 4: val = BPP_SRC_4; break; - case 8: val = BPP_SRC_8; break; - /* case 15: val = BPP_SRC_15; break; */ - case 16: val = BPP_SRC_16; break; - /* case 24: val = BPP_SRC_24; break; */ - case 32: val = BPP_SRC_32; break; - default: val = s->bpp; break; - } - s->bpp = val; - break; - - case FB_COLOR_ORDER: - s->rgb = (val != 0); - break; - - case FB_BYTE_ORDER: - s->endian = (val != 0); - break; - - case FB_PIXEL_ORDER: - /* TODO: Implement this. */ - break; - - case FB_ROW_PITCH: - s->pitch = val; - break; - - case FB_ENABLED: - s->enabled = val; - break; - - default: - if ((offset >> 2) >= FB_PALETTE_START - && (offset >> 2) <= FB_PALETTE_END) { - s->raw_palette[(offset >> 2) - FB_PALETTE_START] = val; - } else { - cpu_abort (cpu_single_env, "syborg_fb_write: Bad offset %x\n", - (int)offset); - } - break; - } -} - -static const MemoryRegionOps syborg_fb_ops = { - .read = syborg_fb_read, - .write = syborg_fb_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void syborg_fb_save(QEMUFile *f, void *opaque) -{ - SyborgFBState *s = opaque; - int i; - - qemu_put_be32(f, s->need_int); - qemu_put_be32(f, s->int_status); - qemu_put_be32(f, s->int_enable); - qemu_put_be32(f, s->enabled); - qemu_put_be32(f, s->base); - qemu_put_be32(f, s->pitch); - qemu_put_be32(f, s->rows); - qemu_put_be32(f, s->cols); - qemu_put_be32(f, s->bpp); - qemu_put_be32(f, s->rgb); - for (i = 0; i < 256; i++) { - qemu_put_be32(f, s->raw_palette[i]); - } -} - -static int syborg_fb_load(QEMUFile *f, void *opaque, int version_id) -{ - SyborgFBState *s = opaque; - int i; - - if (version_id != 1) - return -EINVAL; - - s->need_int = qemu_get_be32(f); - s->int_status = qemu_get_be32(f); - s->int_enable = qemu_get_be32(f); - s->enabled = qemu_get_be32(f); - s->base = qemu_get_be32(f); - s->pitch = qemu_get_be32(f); - s->rows = qemu_get_be32(f); - s->cols = qemu_get_be32(f); - s->bpp = qemu_get_be32(f); - s->rgb = qemu_get_be32(f); - for (i = 0; i < 256; i++) { - s->raw_palette[i] = qemu_get_be32(f); - } - s->need_update = 1; - - return 0; -} - -static int syborg_fb_init(SysBusDevice *dev) -{ - SyborgFBState *s = FROM_SYSBUS(SyborgFBState, dev); - - sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->iomem, &syborg_fb_ops, s, - "framebuffer", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - - s->ds = graphic_console_init(syborg_fb_update_display, - syborg_fb_invalidate_display, - NULL, NULL, s); - - if (s->cols != 0 && s->rows != 0) { - qemu_console_resize(s->ds, s->cols, s->rows); - } - - if (!s->cols) - s->cols = ds_get_width(s->ds); - if (!s->rows) - s->rows = ds_get_height(s->ds); - - register_savevm(&dev->qdev, "syborg_framebuffer", -1, 1, - syborg_fb_save, syborg_fb_load, s); - return 0; -} - -static SysBusDeviceInfo syborg_fb_info = { - .init = syborg_fb_init, - .qdev.name = "syborg,framebuffer", - .qdev.size = sizeof(SyborgFBState), - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("width", SyborgFBState, cols, 0), - DEFINE_PROP_UINT32("height", SyborgFBState, rows, 0), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void syborg_fb_register_devices(void) -{ - sysbus_register_withprop(&syborg_fb_info); -} - -device_init(syborg_fb_register_devices) diff --git a/hw/syborg_interrupt.c b/hw/syborg_interrupt.c deleted file mode 100644 index 93e81c8660..0000000000 --- a/hw/syborg_interrupt.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Syborg interrupt controller. - * - * Copyright (c) 2008 CodeSourcery - * - * 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 "sysbus.h" -#include "syborg.h" - -//#define DEBUG_SYBORG_INT - -#ifdef DEBUG_SYBORG_INT -#define DPRINTF(fmt, ...) \ -do { printf("syborg_int: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__); \ - exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_int: error: " fmt , ## __VA_ARGS__);} while (0) -#endif -enum { - INT_ID = 0, - INT_STATUS = 1, /* number of pending interrupts */ - INT_CURRENT = 2, /* next interrupt to be serviced */ - INT_DISABLE_ALL = 3, - INT_DISABLE = 4, - INT_ENABLE = 5, - INT_TOTAL = 6 -}; - -typedef struct { - unsigned level:1; - unsigned enabled:1; -} syborg_int_flags; - -typedef struct { - SysBusDevice busdev; - MemoryRegion iomem; - int pending_count; - uint32_t num_irqs; - syborg_int_flags *flags; - qemu_irq parent_irq; -} SyborgIntState; - -static void syborg_int_update(SyborgIntState *s) -{ - DPRINTF("pending %d\n", s->pending_count); - qemu_set_irq(s->parent_irq, s->pending_count > 0); -} - -static void syborg_int_set_irq(void *opaque, int irq, int level) -{ - SyborgIntState *s = (SyborgIntState *)opaque; - - if (s->flags[irq].level == level) - return; - - s->flags[irq].level = level; - if (s->flags[irq].enabled) { - if (level) - s->pending_count++; - else - s->pending_count--; - syborg_int_update(s); - } -} - -static uint64_t syborg_int_read(void *opaque, target_phys_addr_t offset, - unsigned size) -{ - SyborgIntState *s = (SyborgIntState *)opaque; - int i; - - offset &= 0xfff; - switch (offset >> 2) { - case INT_ID: - return SYBORG_ID_INT; - case INT_STATUS: - DPRINTF("read status=%d\n", s->pending_count); - return s->pending_count; - - case INT_CURRENT: - for (i = 0; i < s->num_irqs; i++) { - if (s->flags[i].level & s->flags[i].enabled) { - DPRINTF("read current=%d\n", i); - return i; - } - } - DPRINTF("read current=none\n"); - return 0xffffffffu; - - default: - cpu_abort(cpu_single_env, "syborg_int_read: Bad offset %x\n", - (int)offset); - return 0; - } -} - -static void syborg_int_write(void *opaque, target_phys_addr_t offset, - uint64_t value, unsigned size) -{ - SyborgIntState *s = (SyborgIntState *)opaque; - int i; - offset &= 0xfff; - - DPRINTF("syborg_int_write offset=%d val=%d\n", (int)offset, (int)value); - switch (offset >> 2) { - case INT_DISABLE_ALL: - s->pending_count = 0; - for (i = 0; i < s->num_irqs; i++) - s->flags[i].enabled = 0; - break; - - case INT_DISABLE: - if (value >= s->num_irqs) - break; - if (s->flags[value].enabled) { - if (s->flags[value].enabled) - s->pending_count--; - s->flags[value].enabled = 0; - } - break; - - case INT_ENABLE: - if (value >= s->num_irqs) - break; - if (!(s->flags[value].enabled)) { - if(s->flags[value].level) - s->pending_count++; - s->flags[value].enabled = 1; - } - break; - - default: - cpu_abort(cpu_single_env, "syborg_int_write: Bad offset %x\n", - (int)offset); - return; - } - syborg_int_update(s); -} - -static const MemoryRegionOps syborg_int_ops = { - .read = syborg_int_read, - .write = syborg_int_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void syborg_int_save(QEMUFile *f, void *opaque) -{ - SyborgIntState *s = (SyborgIntState *)opaque; - int i; - - qemu_put_be32(f, s->num_irqs); - qemu_put_be32(f, s->pending_count); - for (i = 0; i < s->num_irqs; i++) { - qemu_put_be32(f, s->flags[i].enabled - | ((unsigned)s->flags[i].level << 1)); - } -} - -static int syborg_int_load(QEMUFile *f, void *opaque, int version_id) -{ - SyborgIntState *s = (SyborgIntState *)opaque; - uint32_t val; - int i; - - if (version_id != 1) - return -EINVAL; - - val = qemu_get_be32(f); - if (val != s->num_irqs) - return -EINVAL; - s->pending_count = qemu_get_be32(f); - for (i = 0; i < s->num_irqs; i++) { - val = qemu_get_be32(f); - s->flags[i].enabled = val & 1; - s->flags[i].level = (val >> 1) & 1; - } - return 0; -} - -static int syborg_int_init(SysBusDevice *dev) -{ - SyborgIntState *s = FROM_SYSBUS(SyborgIntState, dev); - - sysbus_init_irq(dev, &s->parent_irq); - qdev_init_gpio_in(&dev->qdev, syborg_int_set_irq, s->num_irqs); - memory_region_init_io(&s->iomem, &syborg_int_ops, s, - "interrupt", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - s->flags = g_malloc0(s->num_irqs * sizeof(syborg_int_flags)); - - register_savevm(&dev->qdev, "syborg_int", -1, 1, syborg_int_save, - syborg_int_load, s); - return 0; -} - -static SysBusDeviceInfo syborg_int_info = { - .init = syborg_int_init, - .qdev.name = "syborg,interrupt", - .qdev.size = sizeof(SyborgIntState), - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("num-interrupts", SyborgIntState, num_irqs, 64), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void syborg_interrupt_register_devices(void) -{ - sysbus_register_withprop(&syborg_int_info); -} - -device_init(syborg_interrupt_register_devices) diff --git a/hw/syborg_keyboard.c b/hw/syborg_keyboard.c deleted file mode 100644 index 942a7dc800..0000000000 --- a/hw/syborg_keyboard.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Syborg keyboard controller. - * - * Copyright (c) 2008 CodeSourcery - * - * 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 "sysbus.h" -#include "console.h" -#include "syborg.h" - -//#define DEBUG_SYBORG_KEYBOARD - -#ifdef DEBUG_SYBORG_KEYBOARD -#define DPRINTF(fmt, ...) \ -do { printf("syborg_keyboard: " fmt , ##args); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_keyboard: error: " fmt , ## __VA_ARGS__); \ - exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_keyboard: error: " fmt , ## __VA_ARGS__); \ -} while (0) -#endif - -enum { - KBD_ID = 0, - KBD_DATA = 1, - KBD_FIFO_COUNT = 2, - KBD_INT_ENABLE = 3, - KBD_FIFO_SIZE = 4 -}; - -typedef struct { - SysBusDevice busdev; - MemoryRegion iomem; - uint32_t int_enabled; - int extension_bit; - uint32_t fifo_size; - uint32_t *key_fifo; - uint32_t read_pos, read_count; - qemu_irq irq; -} SyborgKeyboardState; - -static void syborg_keyboard_update(SyborgKeyboardState *s) -{ - int level = s->read_count && s->int_enabled; - DPRINTF("Update IRQ %d\n", level); - qemu_set_irq(s->irq, level); -} - -static uint64_t syborg_keyboard_read(void *opaque, target_phys_addr_t offset, - unsigned size) -{ - SyborgKeyboardState *s = (SyborgKeyboardState *)opaque; - int c; - - DPRINTF("reg read %d\n", (int)offset); - offset &= 0xfff; - switch (offset >> 2) { - case KBD_ID: - return SYBORG_ID_KEYBOARD; - case KBD_FIFO_COUNT: - return s->read_count; - case KBD_DATA: - if (s->read_count == 0) { - c = -1; - DPRINTF("FIFO underflow\n"); - } else { - c = s->key_fifo[s->read_pos]; - DPRINTF("FIFO read 0x%x\n", c); - s->read_count--; - s->read_pos++; - if (s->read_pos == s->fifo_size) - s->read_pos = 0; - } - syborg_keyboard_update(s); - return c; - case KBD_INT_ENABLE: - return s->int_enabled; - case KBD_FIFO_SIZE: - return s->fifo_size; - default: - cpu_abort(cpu_single_env, "syborg_keyboard_read: Bad offset %x\n", - (int)offset); - return 0; - } -} - -static void syborg_keyboard_write(void *opaque, target_phys_addr_t offset, - uint64_t value, unsigned size) -{ - SyborgKeyboardState *s = (SyborgKeyboardState *)opaque; - - DPRINTF("reg write %d\n", (int)offset); - offset &= 0xfff; - switch (offset >> 2) { - case KBD_INT_ENABLE: - s->int_enabled = value; - syborg_keyboard_update(s); - break; - default: - cpu_abort(cpu_single_env, "syborg_keyboard_write: Bad offset %x\n", - (int)offset); - } -} - -static const MemoryRegionOps syborg_keyboard_ops = { - .read = syborg_keyboard_read, - .write = syborg_keyboard_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void syborg_keyboard_event(void *opaque, int keycode) -{ - SyborgKeyboardState *s = (SyborgKeyboardState *)opaque; - int slot; - uint32_t val; - - /* Strip off 0xe0 prefixes and reconstruct the full scancode. */ - if (keycode == 0xe0 && !s->extension_bit) { - DPRINTF("Extension bit\n"); - s->extension_bit = 0x80; - return; - } - val = (keycode & 0x7f) | s->extension_bit; - if (keycode & 0x80) - val |= 0x80000000u; - s->extension_bit = 0; - - DPRINTF("FIFO push 0x%x\n", val); - slot = s->read_pos + s->read_count; - if (slot >= s->fifo_size) - slot -= s->fifo_size; - - if (s->read_count < s->fifo_size) { - s->read_count++; - s->key_fifo[slot] = val; - } else { - fprintf(stderr, "syborg_keyboard error! FIFO overflow\n"); - } - - syborg_keyboard_update(s); -} - -static const VMStateDescription vmstate_syborg_keyboard = { - .name = "syborg_keyboard", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_EQUAL(fifo_size, SyborgKeyboardState), - VMSTATE_UINT32(int_enabled, SyborgKeyboardState), - VMSTATE_UINT32(read_pos, SyborgKeyboardState), - VMSTATE_UINT32(read_count, SyborgKeyboardState), - VMSTATE_VARRAY_UINT32(key_fifo, SyborgKeyboardState, fifo_size, 1, - vmstate_info_uint32, uint32), - VMSTATE_END_OF_LIST() - } -}; - -static int syborg_keyboard_init(SysBusDevice *dev) -{ - SyborgKeyboardState *s = FROM_SYSBUS(SyborgKeyboardState, dev); - - sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->iomem, &syborg_keyboard_ops, s, - "keyboard", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - if (s->fifo_size <= 0) { - fprintf(stderr, "syborg_keyboard: fifo too small\n"); - s->fifo_size = 16; - } - s->key_fifo = g_malloc0(s->fifo_size * sizeof(s->key_fifo[0])); - - qemu_add_kbd_event_handler(syborg_keyboard_event, s); - - vmstate_register(&dev->qdev, -1, &vmstate_syborg_keyboard, s); - return 0; -} - -static SysBusDeviceInfo syborg_keyboard_info = { - .init = syborg_keyboard_init, - .qdev.name = "syborg,keyboard", - .qdev.size = sizeof(SyborgKeyboardState), - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("fifo-size", SyborgKeyboardState, fifo_size, 16), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void syborg_keyboard_register_devices(void) -{ - sysbus_register_withprop(&syborg_keyboard_info); -} - -device_init(syborg_keyboard_register_devices) diff --git a/hw/syborg_pointer.c b/hw/syborg_pointer.c deleted file mode 100644 index bb75fa42c8..0000000000 --- a/hw/syborg_pointer.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Syborg pointing device (mouse/touchscreen) - * - * Copyright (c) 2008 CodeSourcery - * - * 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 "sysbus.h" -#include "console.h" -#include "syborg.h" - -enum { - POINTER_ID = 0, - POINTER_LATCH = 1, - POINTER_FIFO_COUNT = 2, - POINTER_X = 3, - POINTER_Y = 4, - POINTER_Z = 5, - POINTER_BUTTONS = 6, - POINTER_INT_ENABLE = 7, - POINTER_FIFO_SIZE = 8 -}; - -typedef struct { - int x, y, z, pointer_buttons; -} event_data; - -typedef struct { - SysBusDevice busdev; - MemoryRegion iomem; - int int_enabled; - uint32_t fifo_size; - event_data *event_fifo; - int read_pos, read_count; - qemu_irq irq; - uint32_t absolute; -} SyborgPointerState; - -static void syborg_pointer_update(SyborgPointerState *s) -{ - qemu_set_irq(s->irq, s->read_count && s->int_enabled); -} - -static uint64_t syborg_pointer_read(void *opaque, target_phys_addr_t offset, - unsigned size) -{ - SyborgPointerState *s = (SyborgPointerState *)opaque; - - offset &= 0xfff; - switch (offset >> 2) { - case POINTER_ID: - return s->absolute ? SYBORG_ID_TOUCHSCREEN : SYBORG_ID_MOUSE; - case POINTER_FIFO_COUNT: - return s->read_count; - case POINTER_X: - return s->event_fifo[s->read_pos].x; - case POINTER_Y: - return s->event_fifo[s->read_pos].y; - case POINTER_Z: - return s->event_fifo[s->read_pos].z; - case POINTER_BUTTONS: - return s->event_fifo[s->read_pos].pointer_buttons; - case POINTER_INT_ENABLE: - return s->int_enabled; - case POINTER_FIFO_SIZE: - return s->fifo_size; - default: - cpu_abort(cpu_single_env, "syborg_pointer_read: Bad offset %x\n", - (int)offset); - return 0; - } -} - -static void syborg_pointer_write(void *opaque, target_phys_addr_t offset, - uint64_t value, unsigned size) -{ - SyborgPointerState *s = (SyborgPointerState *)opaque; - - offset &= 0xfff; - switch (offset >> 2) { - case POINTER_LATCH: - if (s->read_count > 0) { - s->read_count--; - if (++s->read_pos == s->fifo_size) - s->read_pos = 0; - } - break; - case POINTER_INT_ENABLE: - s->int_enabled = value; - break; - default: - cpu_abort(cpu_single_env, "syborg_pointer_write: Bad offset %x\n", - (int)offset); - } - syborg_pointer_update(s); -} - -static const MemoryRegionOps syborg_pointer_ops = { - .read = syborg_pointer_read, - .write = syborg_pointer_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void syborg_pointer_event(void *opaque, int dx, int dy, int dz, - int buttons_state) -{ - SyborgPointerState *s = (SyborgPointerState *)opaque; - int slot = s->read_pos + s->read_count; - - /* This first FIFO entry is used to store current register state. */ - if (s->read_count < s->fifo_size - 1) { - s->read_count++; - slot++; - } - - if (slot >= s->fifo_size) - slot -= s->fifo_size; - - if (s->read_count == s->fifo_size && !s->absolute) { - /* Merge existing entries. */ - s->event_fifo[slot].x += dx; - s->event_fifo[slot].y += dy; - s->event_fifo[slot].z += dz; - } else { - s->event_fifo[slot].x = dx; - s->event_fifo[slot].y = dy; - s->event_fifo[slot].z = dz; - } - s->event_fifo[slot].pointer_buttons = buttons_state; - - syborg_pointer_update(s); -} - -static const VMStateDescription vmstate_event_data = { - .name = "dbma_channel", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .fields = (VMStateField[]) { - VMSTATE_INT32(x, event_data), - VMSTATE_INT32(y, event_data), - VMSTATE_INT32(z, event_data), - VMSTATE_INT32(pointer_buttons, event_data), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_syborg_pointer = { - .name = "syborg_pointer", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_EQUAL(fifo_size, SyborgPointerState), - VMSTATE_UINT32_EQUAL(absolute, SyborgPointerState), - VMSTATE_INT32(int_enabled, SyborgPointerState), - VMSTATE_INT32(read_pos, SyborgPointerState), - VMSTATE_INT32(read_count, SyborgPointerState), - VMSTATE_STRUCT_VARRAY_UINT32(event_fifo, SyborgPointerState, fifo_size, - 1, vmstate_event_data, event_data), - VMSTATE_END_OF_LIST() - } -}; - -static int syborg_pointer_init(SysBusDevice *dev) -{ - SyborgPointerState *s = FROM_SYSBUS(SyborgPointerState, dev); - - sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->iomem, &syborg_pointer_ops, s, - "pointer", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - - if (s->fifo_size <= 0) { - fprintf(stderr, "syborg_pointer: fifo too small\n"); - s->fifo_size = 16; - } - s->event_fifo = g_malloc0(s->fifo_size * sizeof(s->event_fifo[0])); - - qemu_add_mouse_event_handler(syborg_pointer_event, s, s->absolute, - "Syborg Pointer"); - - vmstate_register(&dev->qdev, -1, &vmstate_syborg_pointer, s); - return 0; -} - -static SysBusDeviceInfo syborg_pointer_info = { - .init = syborg_pointer_init, - .qdev.name = "syborg,pointer", - .qdev.size = sizeof(SyborgPointerState), - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("fifo-size", SyborgPointerState, fifo_size, 16), - DEFINE_PROP_UINT32("absolute", SyborgPointerState, absolute, 1), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void syborg_pointer_register_devices(void) -{ - sysbus_register_withprop(&syborg_pointer_info); -} - -device_init(syborg_pointer_register_devices) diff --git a/hw/syborg_rtc.c b/hw/syborg_rtc.c deleted file mode 100644 index b5f34798b6..0000000000 --- a/hw/syborg_rtc.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Syborg RTC - * - * Copyright (c) 2008 CodeSourcery - * - * 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 "sysbus.h" -#include "qemu-timer.h" -#include "syborg.h" - -enum { - RTC_ID = 0, - RTC_LATCH = 1, - RTC_DATA_LOW = 2, - RTC_DATA_HIGH = 3 -}; - -typedef struct { - SysBusDevice busdev; - MemoryRegion iomem; - int64_t offset; - int64_t data; - qemu_irq irq; -} SyborgRTCState; - -static uint64_t syborg_rtc_read(void *opaque, target_phys_addr_t offset, - unsigned size) -{ - SyborgRTCState *s = (SyborgRTCState *)opaque; - offset &= 0xfff; - switch (offset >> 2) { - case RTC_ID: - return SYBORG_ID_RTC; - case RTC_DATA_LOW: - return (uint32_t)s->data; - case RTC_DATA_HIGH: - return (uint32_t)(s->data >> 32); - default: - cpu_abort(cpu_single_env, "syborg_rtc_read: Bad offset %x\n", - (int)offset); - return 0; - } -} - -static void syborg_rtc_write(void *opaque, target_phys_addr_t offset, - uint64_t value, unsigned size) -{ - SyborgRTCState *s = (SyborgRTCState *)opaque; - uint64_t now; - - offset &= 0xfff; - switch (offset >> 2) { - case RTC_LATCH: - now = qemu_get_clock_ns(vm_clock); - if (value >= 4) { - s->offset = s->data - now; - } else { - s->data = now + s->offset; - while (value) { - s->data /= 1000; - value--; - } - } - break; - case RTC_DATA_LOW: - s->data = (s->data & ~(uint64_t)0xffffffffu) | value; - break; - case RTC_DATA_HIGH: - s->data = (s->data & 0xffffffffu) | ((uint64_t)value << 32); - break; - default: - cpu_abort(cpu_single_env, "syborg_rtc_write: Bad offset %x\n", - (int)offset); - break; - } -} - -static const MemoryRegionOps syborg_rtc_ops = { - .read = syborg_rtc_read, - .write = syborg_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_syborg_rtc = { - .name = "syborg_keyboard", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_INT64(offset, SyborgRTCState), - VMSTATE_INT64(data, SyborgRTCState), - VMSTATE_END_OF_LIST() - } -}; - -static int syborg_rtc_init(SysBusDevice *dev) -{ - SyborgRTCState *s = FROM_SYSBUS(SyborgRTCState, dev); - struct tm tm; - - memory_region_init_io(&s->iomem, &syborg_rtc_ops, s, "rtc", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - - qemu_get_timedate(&tm, 0); - s->offset = (uint64_t)mktime(&tm) * 1000000000; - - vmstate_register(&dev->qdev, -1, &vmstate_syborg_rtc, s); - return 0; -} - -static void syborg_rtc_register_devices(void) -{ - sysbus_register_dev("syborg,rtc", sizeof(SyborgRTCState), syborg_rtc_init); -} - -device_init(syborg_rtc_register_devices) diff --git a/hw/syborg_serial.c b/hw/syborg_serial.c deleted file mode 100644 index 6f339fa7a6..0000000000 --- a/hw/syborg_serial.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Syborg serial port - * - * Copyright (c) 2008 CodeSourcery - * - * 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 "sysbus.h" -#include "qemu-char.h" -#include "syborg.h" - -//#define DEBUG_SYBORG_SERIAL - -#ifdef DEBUG_SYBORG_SERIAL -#define DPRINTF(fmt, ...) \ -do { printf("syborg_serial: " fmt , ##args); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_serial: error: " fmt , ## __VA_ARGS__); \ - exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_serial: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -enum { - SERIAL_ID = 0, - SERIAL_DATA = 1, - SERIAL_FIFO_COUNT = 2, - SERIAL_INT_ENABLE = 3, - SERIAL_DMA_TX_ADDR = 4, - SERIAL_DMA_TX_COUNT = 5, /* triggers dma */ - SERIAL_DMA_RX_ADDR = 6, - SERIAL_DMA_RX_COUNT = 7, /* triggers dma */ - SERIAL_FIFO_SIZE = 8 -}; - -#define SERIAL_INT_FIFO (1u << 0) -#define SERIAL_INT_DMA_TX (1u << 1) -#define SERIAL_INT_DMA_RX (1u << 2) - -typedef struct { - SysBusDevice busdev; - MemoryRegion iomem; - uint32_t int_enable; - uint32_t fifo_size; - uint32_t *read_fifo; - int read_pos; - int read_count; - CharDriverState *chr; - qemu_irq irq; - uint32_t dma_tx_ptr; - uint32_t dma_rx_ptr; - uint32_t dma_rx_size; -} SyborgSerialState; - -static void syborg_serial_update(SyborgSerialState *s) -{ - int level; - level = 0; - if ((s->int_enable & SERIAL_INT_FIFO) && s->read_count) - level = 1; - if (s->int_enable & SERIAL_INT_DMA_TX) - level = 1; - if ((s->int_enable & SERIAL_INT_DMA_RX) && s->dma_rx_size == 0) - level = 1; - - qemu_set_irq(s->irq, level); -} - -static uint32_t fifo_pop(SyborgSerialState *s) -{ - const uint32_t c = s->read_fifo[s->read_pos]; - s->read_count--; - s->read_pos++; - if (s->read_pos == s->fifo_size) - s->read_pos = 0; - - DPRINTF("FIFO pop %x (%d)\n", c, s->read_count); - return c; -} - -static void fifo_push(SyborgSerialState *s, uint32_t new_value) -{ - int slot; - - DPRINTF("FIFO push %x (%d)\n", new_value, s->read_count); - slot = s->read_pos + s->read_count; - if (slot >= s->fifo_size) - slot -= s->fifo_size; - s->read_fifo[slot] = new_value; - s->read_count++; -} - -static void do_dma_tx(SyborgSerialState *s, uint32_t count) -{ - unsigned char ch; - - if (count == 0) - return; - - if (s->chr != NULL) { - /* optimize later. Now, 1 byte per iteration */ - while (count--) { - cpu_physical_memory_read(s->dma_tx_ptr, &ch, 1); - qemu_chr_fe_write(s->chr, &ch, 1); - s->dma_tx_ptr++; - } - } else { - s->dma_tx_ptr += count; - } - /* QEMU char backends do not have a nonblocking mode, so we transmit all - the data immediately and the interrupt status will be unchanged. */ -} - -/* Initiate RX DMA, and transfer data from the FIFO. */ -static void dma_rx_start(SyborgSerialState *s, uint32_t len) -{ - uint32_t dest; - unsigned char ch; - - dest = s->dma_rx_ptr; - if (s->read_count < len) { - s->dma_rx_size = len - s->read_count; - len = s->read_count; - } else { - s->dma_rx_size = 0; - } - - while (len--) { - ch = fifo_pop(s); - cpu_physical_memory_write(dest, &ch, 1); - dest++; - } - s->dma_rx_ptr = dest; - syborg_serial_update(s); -} - -static uint64_t syborg_serial_read(void *opaque, target_phys_addr_t offset, - unsigned size) -{ - SyborgSerialState *s = (SyborgSerialState *)opaque; - uint32_t c; - - offset &= 0xfff; - DPRINTF("read 0x%x\n", (int)offset); - switch(offset >> 2) { - case SERIAL_ID: - return SYBORG_ID_SERIAL; - case SERIAL_DATA: - if (s->read_count > 0) - c = fifo_pop(s); - else - c = -1; - syborg_serial_update(s); - return c; - case SERIAL_FIFO_COUNT: - return s->read_count; - case SERIAL_INT_ENABLE: - return s->int_enable; - case SERIAL_DMA_TX_ADDR: - return s->dma_tx_ptr; - case SERIAL_DMA_TX_COUNT: - return 0; - case SERIAL_DMA_RX_ADDR: - return s->dma_rx_ptr; - case SERIAL_DMA_RX_COUNT: - return s->dma_rx_size; - case SERIAL_FIFO_SIZE: - return s->fifo_size; - - default: - cpu_abort(cpu_single_env, "syborg_serial_read: Bad offset %x\n", - (int)offset); - return 0; - } -} - -static void syborg_serial_write(void *opaque, target_phys_addr_t offset, - uint64_t value, unsigned size) -{ - SyborgSerialState *s = (SyborgSerialState *)opaque; - unsigned char ch; - - offset &= 0xfff; - DPRINTF("Write 0x%x=0x%x\n", (int)offset, value); - switch (offset >> 2) { - case SERIAL_DATA: - ch = value; - if (s->chr) - qemu_chr_fe_write(s->chr, &ch, 1); - break; - case SERIAL_INT_ENABLE: - s->int_enable = value; - syborg_serial_update(s); - break; - case SERIAL_DMA_TX_ADDR: - s->dma_tx_ptr = value; - break; - case SERIAL_DMA_TX_COUNT: - do_dma_tx(s, value); - break; - case SERIAL_DMA_RX_ADDR: - /* For safety, writes to this register cancel any pending DMA. */ - s->dma_rx_size = 0; - s->dma_rx_ptr = value; - break; - case SERIAL_DMA_RX_COUNT: - dma_rx_start(s, value); - break; - default: - cpu_abort(cpu_single_env, "syborg_serial_write: Bad offset %x\n", - (int)offset); - break; - } -} - -static int syborg_serial_can_receive(void *opaque) -{ - SyborgSerialState *s = (SyborgSerialState *)opaque; - - if (s->dma_rx_size) - return s->dma_rx_size; - return s->fifo_size - s->read_count; -} - -static void syborg_serial_receive(void *opaque, const uint8_t *buf, int size) -{ - SyborgSerialState *s = (SyborgSerialState *)opaque; - - if (s->dma_rx_size) { - /* Place it in the DMA buffer. */ - cpu_physical_memory_write(s->dma_rx_ptr, buf, size); - s->dma_rx_size -= size; - s->dma_rx_ptr += size; - } else { - while (size--) - fifo_push(s, *buf); - } - - syborg_serial_update(s); -} - -static void syborg_serial_event(void *opaque, int event) -{ - /* TODO: Report BREAK events? */ -} - -static const MemoryRegionOps syborg_serial_ops = { - .read = syborg_serial_read, - .write = syborg_serial_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_syborg_serial = { - .name = "syborg_serial", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_EQUAL(fifo_size, SyborgSerialState), - VMSTATE_UINT32(int_enable, SyborgSerialState), - VMSTATE_INT32(read_pos, SyborgSerialState), - VMSTATE_INT32(read_count, SyborgSerialState), - VMSTATE_UINT32(dma_tx_ptr, SyborgSerialState), - VMSTATE_UINT32(dma_rx_ptr, SyborgSerialState), - VMSTATE_UINT32(dma_rx_size, SyborgSerialState), - VMSTATE_VARRAY_UINT32(read_fifo, SyborgSerialState, fifo_size, 1, - vmstate_info_uint32, uint32), - VMSTATE_END_OF_LIST() - } -}; - -static int syborg_serial_init(SysBusDevice *dev) -{ - SyborgSerialState *s = FROM_SYSBUS(SyborgSerialState, dev); - - sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->iomem, &syborg_serial_ops, s, - "serial", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - s->chr = qdev_init_chardev(&dev->qdev); - if (s->chr) { - qemu_chr_add_handlers(s->chr, syborg_serial_can_receive, - syborg_serial_receive, syborg_serial_event, s); - } - if (s->fifo_size <= 0) { - fprintf(stderr, "syborg_serial: fifo too small\n"); - s->fifo_size = 16; - } - s->read_fifo = g_malloc0(s->fifo_size * sizeof(s->read_fifo[0])); - - return 0; -} - -static SysBusDeviceInfo syborg_serial_info = { - .init = syborg_serial_init, - .qdev.name = "syborg,serial", - .qdev.size = sizeof(SyborgSerialState), - .qdev.vmsd = &vmstate_syborg_serial, - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("fifo-size", SyborgSerialState, fifo_size, 16), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void syborg_serial_register_devices(void) -{ - sysbus_register_withprop(&syborg_serial_info); -} - -device_init(syborg_serial_register_devices) diff --git a/hw/syborg_timer.c b/hw/syborg_timer.c deleted file mode 100644 index 1498f01a62..0000000000 --- a/hw/syborg_timer.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Syborg Interval Timer. - * - * Copyright (c) 2008 CodeSourcery - * - * 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 "sysbus.h" -#include "qemu-timer.h" -#include "syborg.h" - -//#define DEBUG_SYBORG_TIMER - -#ifdef DEBUG_SYBORG_TIMER -#define DPRINTF(fmt, ...) \ -do { printf("syborg_timer: " fmt , ##args); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_timer: error: " fmt , ## __VA_ARGS__); \ - exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_timer: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -enum { - TIMER_ID = 0, - TIMER_RUNNING = 1, - TIMER_ONESHOT = 2, - TIMER_LIMIT = 3, - TIMER_VALUE = 4, - TIMER_INT_ENABLE = 5, - TIMER_INT_STATUS = 6, - TIMER_FREQ = 7 -}; - -typedef struct { - SysBusDevice busdev; - MemoryRegion iomem; - ptimer_state *timer; - int running; - int oneshot; - uint32_t limit; - uint32_t freq; - uint32_t int_level; - uint32_t int_enabled; - qemu_irq irq; -} SyborgTimerState; - -static void syborg_timer_update(SyborgTimerState *s) -{ - /* Update interrupt. */ - if (s->int_level && s->int_enabled) { - qemu_irq_raise(s->irq); - } else { - qemu_irq_lower(s->irq); - } -} - -static void syborg_timer_tick(void *opaque) -{ - SyborgTimerState *s = (SyborgTimerState *)opaque; - //DPRINTF("Timer Tick\n"); - s->int_level = 1; - if (s->oneshot) - s->running = 0; - syborg_timer_update(s); -} - -static uint64_t syborg_timer_read(void *opaque, target_phys_addr_t offset, - unsigned size) -{ - SyborgTimerState *s = (SyborgTimerState *)opaque; - - DPRINTF("Reg read %d\n", (int)offset); - offset &= 0xfff; - switch (offset >> 2) { - case TIMER_ID: - return SYBORG_ID_TIMER; - case TIMER_RUNNING: - return s->running; - case TIMER_ONESHOT: - return s->oneshot; - case TIMER_LIMIT: - return s->limit; - case TIMER_VALUE: - return ptimer_get_count(s->timer); - case TIMER_INT_ENABLE: - return s->int_enabled; - case TIMER_INT_STATUS: - return s->int_level; - case TIMER_FREQ: - return s->freq; - default: - cpu_abort(cpu_single_env, "syborg_timer_read: Bad offset %x\n", - (int)offset); - return 0; - } -} - -static void syborg_timer_write(void *opaque, target_phys_addr_t offset, - uint64_t value, unsigned size) -{ - SyborgTimerState *s = (SyborgTimerState *)opaque; - - DPRINTF("Reg write %d\n", (int)offset); - offset &= 0xfff; - switch (offset >> 2) { - case TIMER_RUNNING: - if (value == s->running) - break; - s->running = value; - if (value) { - ptimer_run(s->timer, s->oneshot); - } else { - ptimer_stop(s->timer); - } - break; - case TIMER_ONESHOT: - if (s->running) { - ptimer_stop(s->timer); - } - s->oneshot = value; - if (s->running) { - ptimer_run(s->timer, s->oneshot); - } - break; - case TIMER_LIMIT: - s->limit = value; - ptimer_set_limit(s->timer, value, 1); - break; - case TIMER_VALUE: - ptimer_set_count(s->timer, value); - break; - case TIMER_INT_ENABLE: - s->int_enabled = value; - syborg_timer_update(s); - break; - case TIMER_INT_STATUS: - s->int_level &= ~value; - syborg_timer_update(s); - break; - default: - cpu_abort(cpu_single_env, "syborg_timer_write: Bad offset %x\n", - (int)offset); - break; - } -} - -static const MemoryRegionOps syborg_timer_ops = { - .read = syborg_timer_read, - .write = syborg_timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_syborg_timer = { - .name = "syborg_timer", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(running, SyborgTimerState), - VMSTATE_INT32(oneshot, SyborgTimerState), - VMSTATE_UINT32(limit, SyborgTimerState), - VMSTATE_UINT32(int_level, SyborgTimerState), - VMSTATE_UINT32(int_enabled, SyborgTimerState), - VMSTATE_PTIMER(timer, SyborgTimerState), - VMSTATE_END_OF_LIST() - } -}; - -static int syborg_timer_init(SysBusDevice *dev) -{ - SyborgTimerState *s = FROM_SYSBUS(SyborgTimerState, dev); - QEMUBH *bh; - - if (s->freq == 0) { - fprintf(stderr, "syborg_timer: Zero/unset frequency\n"); - exit(1); - } - sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->iomem, &syborg_timer_ops, s, "timer", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - - bh = qemu_bh_new(syborg_timer_tick, s); - s->timer = ptimer_init(bh); - ptimer_set_freq(s->timer, s->freq); - vmstate_register(&dev->qdev, -1, &vmstate_syborg_timer, s); - return 0; -} - -static SysBusDeviceInfo syborg_timer_info = { - .init = syborg_timer_init, - .qdev.name = "syborg,timer", - .qdev.size = sizeof(SyborgTimerState), - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("frequency",SyborgTimerState, freq, 0), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void syborg_timer_register_devices(void) -{ - sysbus_register_withprop(&syborg_timer_info); -} - -device_init(syborg_timer_register_devices) diff --git a/hw/syborg_virtio.c b/hw/syborg_virtio.c deleted file mode 100644 index 96a0ee651f..0000000000 --- a/hw/syborg_virtio.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Virtio Syborg bindings - * - * Copyright (c) 2009 CodeSourcery - * - * 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 "syborg.h" -#include "sysbus.h" -#include "virtio.h" -#include "virtio-net.h" - -//#define DEBUG_SYBORG_VIRTIO - -#ifdef DEBUG_SYBORG_VIRTIO -#define DPRINTF(fmt, ...) \ -do { printf("syborg_virtio: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_virtio: error: " fmt , ## __VA_ARGS__); \ - exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "syborg_virtio: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -enum { - SYBORG_VIRTIO_ID = 0, - SYBORG_VIRTIO_DEVTYPE = 1, - SYBORG_VIRTIO_HOST_FEATURES = 2, - SYBORG_VIRTIO_GUEST_FEATURES = 3, - SYBORG_VIRTIO_QUEUE_BASE = 4, - SYBORG_VIRTIO_QUEUE_NUM = 5, - SYBORG_VIRTIO_QUEUE_SEL = 6, - SYBORG_VIRTIO_QUEUE_NOTIFY = 7, - SYBORG_VIRTIO_STATUS = 8, - SYBORG_VIRTIO_INT_ENABLE = 9, - SYBORG_VIRTIO_INT_STATUS = 10 -}; - -#define SYBORG_VIRTIO_CONFIG 0x100 - -/* Device independent interface. */ - -typedef struct { - SysBusDevice busdev; - VirtIODevice *vdev; - MemoryRegion iomem; - qemu_irq irq; - uint32_t int_enable; - uint32_t id; - NICConf nic; - uint32_t host_features; - virtio_net_conf net; -} SyborgVirtIOProxy; - -static uint32_t syborg_virtio_readl(void *opaque, target_phys_addr_t offset) -{ - SyborgVirtIOProxy *s = opaque; - VirtIODevice *vdev = s->vdev; - uint32_t ret; - - DPRINTF("readl 0x%x\n", (int)offset); - if (offset >= SYBORG_VIRTIO_CONFIG) { - return virtio_config_readl(vdev, offset - SYBORG_VIRTIO_CONFIG); - } - switch(offset >> 2) { - case SYBORG_VIRTIO_ID: - ret = SYBORG_ID_VIRTIO; - break; - case SYBORG_VIRTIO_DEVTYPE: - ret = s->id; - break; - case SYBORG_VIRTIO_HOST_FEATURES: - ret = s->host_features; - break; - case SYBORG_VIRTIO_GUEST_FEATURES: - ret = vdev->guest_features; - break; - case SYBORG_VIRTIO_QUEUE_BASE: - ret = virtio_queue_get_addr(vdev, vdev->queue_sel); - break; - case SYBORG_VIRTIO_QUEUE_NUM: - ret = virtio_queue_get_num(vdev, vdev->queue_sel); - break; - case SYBORG_VIRTIO_QUEUE_SEL: - ret = vdev->queue_sel; - break; - case SYBORG_VIRTIO_STATUS: - ret = vdev->status; - break; - case SYBORG_VIRTIO_INT_ENABLE: - ret = s->int_enable; - break; - case SYBORG_VIRTIO_INT_STATUS: - ret = vdev->isr; - break; - default: - BADF("Bad read offset 0x%x\n", (int)offset); - return 0; - } - return ret; -} - -static void syborg_virtio_writel(void *opaque, target_phys_addr_t offset, - uint32_t value) -{ - SyborgVirtIOProxy *s = opaque; - VirtIODevice *vdev = s->vdev; - - DPRINTF("writel 0x%x = 0x%x\n", (int)offset, value); - if (offset >= SYBORG_VIRTIO_CONFIG) { - return virtio_config_writel(vdev, offset - SYBORG_VIRTIO_CONFIG, - value); - } - switch (offset >> 2) { - case SYBORG_VIRTIO_GUEST_FEATURES: - virtio_set_features(vdev, value); - break; - case SYBORG_VIRTIO_QUEUE_BASE: - if (value == 0) - virtio_reset(vdev); - else - virtio_queue_set_addr(vdev, vdev->queue_sel, value); - break; - case SYBORG_VIRTIO_QUEUE_SEL: - if (value < VIRTIO_PCI_QUEUE_MAX) - vdev->queue_sel = value; - break; - case SYBORG_VIRTIO_QUEUE_NOTIFY: - if (value < VIRTIO_PCI_QUEUE_MAX) { - virtio_queue_notify(vdev, value); - } - break; - case SYBORG_VIRTIO_STATUS: - virtio_set_status(vdev, value & 0xFF); - if (vdev->status == 0) - virtio_reset(vdev); - break; - case SYBORG_VIRTIO_INT_ENABLE: - s->int_enable = value; - virtio_update_irq(vdev); - break; - case SYBORG_VIRTIO_INT_STATUS: - vdev->isr &= ~value; - virtio_update_irq(vdev); - break; - default: - BADF("Bad write offset 0x%x\n", (int)offset); - break; - } -} - -static uint32_t syborg_virtio_readw(void *opaque, target_phys_addr_t offset) -{ - SyborgVirtIOProxy *s = opaque; - VirtIODevice *vdev = s->vdev; - - DPRINTF("readw 0x%x\n", (int)offset); - if (offset >= SYBORG_VIRTIO_CONFIG) { - return virtio_config_readw(vdev, offset - SYBORG_VIRTIO_CONFIG); - } - BADF("Bad halfword read offset 0x%x\n", (int)offset); - return -1; -} - -static void syborg_virtio_writew(void *opaque, target_phys_addr_t offset, - uint32_t value) -{ - SyborgVirtIOProxy *s = opaque; - VirtIODevice *vdev = s->vdev; - - DPRINTF("writew 0x%x = 0x%x\n", (int)offset, value); - if (offset >= SYBORG_VIRTIO_CONFIG) { - return virtio_config_writew(vdev, offset - SYBORG_VIRTIO_CONFIG, - value); - } - BADF("Bad halfword write offset 0x%x\n", (int)offset); -} - -static uint32_t syborg_virtio_readb(void *opaque, target_phys_addr_t offset) -{ - SyborgVirtIOProxy *s = opaque; - VirtIODevice *vdev = s->vdev; - - DPRINTF("readb 0x%x\n", (int)offset); - if (offset >= SYBORG_VIRTIO_CONFIG) { - return virtio_config_readb(vdev, offset - SYBORG_VIRTIO_CONFIG); - } - BADF("Bad byte read offset 0x%x\n", (int)offset); - return -1; -} - -static void syborg_virtio_writeb(void *opaque, target_phys_addr_t offset, - uint32_t value) -{ - SyborgVirtIOProxy *s = opaque; - VirtIODevice *vdev = s->vdev; - - DPRINTF("writeb 0x%x = 0x%x\n", (int)offset, value); - if (offset >= SYBORG_VIRTIO_CONFIG) { - return virtio_config_writeb(vdev, offset - SYBORG_VIRTIO_CONFIG, - value); - } - BADF("Bad byte write offset 0x%x\n", (int)offset); -} - -static const MemoryRegionOps syborg_virtio_ops = { - .old_mmio = { - .read = { syborg_virtio_readb, syborg_virtio_readw, syborg_virtio_readl }, - .write = { syborg_virtio_writeb, syborg_virtio_writew, syborg_virtio_writel }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void syborg_virtio_update_irq(void *opaque, uint16_t vector) -{ - SyborgVirtIOProxy *proxy = opaque; - int level; - - level = proxy->int_enable & proxy->vdev->isr; - DPRINTF("IRQ %d\n", level); - qemu_set_irq(proxy->irq, level != 0); -} - -static unsigned syborg_virtio_get_features(void *opaque) -{ - SyborgVirtIOProxy *proxy = opaque; - return proxy->host_features; -} - -static VirtIOBindings syborg_virtio_bindings = { - .notify = syborg_virtio_update_irq, - .get_features = syborg_virtio_get_features, -}; - -static int syborg_virtio_init(SyborgVirtIOProxy *proxy, VirtIODevice *vdev) -{ - proxy->vdev = vdev; - - /* Don't support multiple vectors */ - proxy->vdev->nvectors = 0; - sysbus_init_irq(&proxy->busdev, &proxy->irq); - memory_region_init_io(&proxy->iomem, &syborg_virtio_ops, proxy, - "virtio", 0x1000); - sysbus_init_mmio(&proxy->busdev, &proxy->iomem); - - proxy->id = ((uint32_t)0x1af4 << 16) | vdev->device_id; - - qemu_register_reset(virtio_reset, vdev); - - virtio_bind_device(vdev, &syborg_virtio_bindings, proxy); - proxy->host_features |= (0x1 << VIRTIO_F_NOTIFY_ON_EMPTY); - proxy->host_features = vdev->get_features(vdev, proxy->host_features); - return 0; -} - -/* Device specific bindings. */ - -static int syborg_virtio_net_init(SysBusDevice *dev) -{ - VirtIODevice *vdev; - SyborgVirtIOProxy *proxy = FROM_SYSBUS(SyborgVirtIOProxy, dev); - - vdev = virtio_net_init(&dev->qdev, &proxy->nic, &proxy->net); - return syborg_virtio_init(proxy, vdev); -} - -static SysBusDeviceInfo syborg_virtio_net_info = { - .init = syborg_virtio_net_init, - .qdev.name = "syborg,virtio-net", - .qdev.size = sizeof(SyborgVirtIOProxy), - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(SyborgVirtIOProxy, nic), - DEFINE_VIRTIO_NET_FEATURES(SyborgVirtIOProxy, host_features), - DEFINE_PROP_UINT32("x-txtimer", SyborgVirtIOProxy, - net.txtimer, TX_TIMER_INTERVAL), - DEFINE_PROP_INT32("x-txburst", SyborgVirtIOProxy, - net.txburst, TX_BURST), - DEFINE_PROP_STRING("tx", SyborgVirtIOProxy, net.tx), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void syborg_virtio_register_devices(void) -{ - sysbus_register_withprop(&syborg_virtio_net_info); -} - -device_init(syborg_virtio_register_devices) diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index a946e1d1fd..7c926c0d47 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -1109,7 +1109,7 @@ static inline int get_dwords(EHCIState *ehci, uint32_t addr, int i; for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - pci_dma_read(&ehci->dev, addr, (uint8_t *)buf, sizeof(*buf)); + pci_dma_read(&ehci->dev, addr, buf, sizeof(*buf)); *buf = le32_to_cpu(*buf); } @@ -1124,7 +1124,7 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr, for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { uint32_t tmp = cpu_to_le32(*buf); - pci_dma_write(&ehci->dev, addr, (uint8_t *)&tmp, sizeof(tmp)); + pci_dma_write(&ehci->dev, addr, &tmp, sizeof(tmp)); } return 1; @@ -2157,7 +2157,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) } list |= ((ehci->frindex & 0x1ff8) >> 1); - pci_dma_read(&ehci->dev, list, (uint8_t *) &entry, sizeof entry); + pci_dma_read(&ehci->dev, list, &entry, sizeof entry); entry = le32_to_cpu(entry); DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", diff --git a/hw/usb-net.c b/hw/usb-net.c index a8b7c8dd76..f91fa32334 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -1268,8 +1268,9 @@ static ssize_t usbnet_receive(VLANClientState *nc, const uint8_t *buf, size_t si if (is_rndis(s)) { msg = (struct rndis_packet_msg_type *) s->in_buf; - if (!s->rndis_state == RNDIS_DATA_INITIALIZED) + if (s->rndis_state != RNDIS_DATA_INITIALIZED) { return -1; + } if (size + sizeof(struct rndis_packet_msg_type) > sizeof(s->in_buf)) return -1; @@ -1302,7 +1303,7 @@ static int usbnet_can_receive(VLANClientState *nc) { USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; - if (is_rndis(s) && !s->rndis_state == RNDIS_DATA_INITIALIZED) { + if (is_rndis(s) && s->rndis_state != RNDIS_DATA_INITIALIZED) { return 1; } diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index f9e3ea5bfc..f8912e2b0b 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -876,7 +876,7 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet) uint32_t link = async->td; uint32_t int_mask = 0, val; - pci_dma_read(&s->dev, link & ~0xf, (uint8_t *) &td, sizeof(td)); + pci_dma_read(&s->dev, link & ~0xf, &td, sizeof(td)); le32_to_cpus(&td.link); le32_to_cpus(&td.ctrl); le32_to_cpus(&td.token); @@ -888,8 +888,7 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet) /* update the status bits of the TD */ val = cpu_to_le32(td.ctrl); - pci_dma_write(&s->dev, (link & ~0xf) + 4, - (const uint8_t *)&val, sizeof(val)); + pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); uhci_async_free(s, async); } else { async->done = 1; @@ -952,7 +951,7 @@ static void uhci_process_frame(UHCIState *s) DPRINTF("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr); - pci_dma_read(&s->dev, frame_addr, (uint8_t *)&link, 4); + pci_dma_read(&s->dev, frame_addr, &link, 4); le32_to_cpus(&link); int_mask = 0; @@ -976,7 +975,7 @@ static void uhci_process_frame(UHCIState *s) break; } - pci_dma_read(&s->dev, link & ~0xf, (uint8_t *) &qh, sizeof(qh)); + pci_dma_read(&s->dev, link & ~0xf, &qh, sizeof(qh)); le32_to_cpus(&qh.link); le32_to_cpus(&qh.el_link); @@ -996,7 +995,7 @@ static void uhci_process_frame(UHCIState *s) } /* TD */ - pci_dma_read(&s->dev, link & ~0xf, (uint8_t *) &td, sizeof(td)); + pci_dma_read(&s->dev, link & ~0xf, &td, sizeof(td)); le32_to_cpus(&td.link); le32_to_cpus(&td.ctrl); le32_to_cpus(&td.token); @@ -1010,8 +1009,7 @@ static void uhci_process_frame(UHCIState *s) if (old_td_ctrl != td.ctrl) { /* update the status bits of the TD */ val = cpu_to_le32(td.ctrl); - pci_dma_write(&s->dev, (link & ~0xf) + 4, - (const uint8_t *)&val, sizeof(val)); + pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); } if (ret < 0) { @@ -1039,8 +1037,7 @@ static void uhci_process_frame(UHCIState *s) /* update QH element link */ qh.el_link = link; val = cpu_to_le32(qh.el_link); - pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, - (const uint8_t *)&val, sizeof(val)); + pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val)); if (!depth_first(link)) { /* done with this QH */ diff --git a/migration-exec.c b/migration-exec.c index b7b1055e88..e14552ec01 100644 --- a/migration-exec.c +++ b/migration-exec.c @@ -50,12 +50,9 @@ static int exec_close(MigrationState *s) ret = qemu_fclose(s->opaque); s->opaque = NULL; s->fd = -1; - if (ret != -1 && - WIFEXITED(ret) - && WEXITSTATUS(ret) == 0) { - ret = 0; - } else { - ret = -1; + if (ret >= 0 && !(WIFEXITED(ret) && WEXITSTATUS(ret) == 0)) { + /* close succeeded, but non-zero exit code: */ + ret = -EIO; /* fake errno value */ } } return ret; diff --git a/migration-tcp.c b/migration-tcp.c index 5aa742c34b..cf6a9b83d6 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -40,12 +40,15 @@ static int socket_write(MigrationState *s, const void * buf, size_t size) static int tcp_close(MigrationState *s) { + int r = 0; DPRINTF("tcp_close\n"); if (s->fd != -1) { - close(s->fd); + if (close(s->fd) < 0) { + r = -errno; + } s->fd = -1; } - return 0; + return r; } static void tcp_wait_for_connect(void *opaque) diff --git a/migration-unix.c b/migration-unix.c index 8596353d7d..dfcf2033c6 100644 --- a/migration-unix.c +++ b/migration-unix.c @@ -40,12 +40,15 @@ static int unix_write(MigrationState *s, const void * buf, size_t size) static int unix_close(MigrationState *s) { + int r = 0; DPRINTF("unix_close\n"); if (s->fd != -1) { - close(s->fd); + if (close(s->fd) < 0) { + r = -errno; + } s->fd = -1; } - return 0; + return r; } static void unix_wait_for_connect(void *opaque) diff --git a/migration.c b/migration.c index 8280d7189a..412fdfe5bf 100644 --- a/migration.c +++ b/migration.c @@ -174,9 +174,7 @@ static int migrate_fd_cleanup(MigrationState *s) if (s->file) { DPRINTF("closing file\n"); - if (qemu_fclose(s->file) != 0) { - ret = -1; - } + ret = qemu_fclose(s->file); s->file = NULL; } else { if (s->mon) { @@ -468,37 +466,27 @@ int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) return 0; } -int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_migrate_cancel(Error **errp) { migrate_fd_cancel(migrate_get_current()); - return 0; } -int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_migrate_set_speed(int64_t value, Error **errp) { - int64_t d; MigrationState *s; - d = qdict_get_int(qdict, "value"); - if (d < 0) { - d = 0; + if (value < 0) { + value = 0; } s = migrate_get_current(); - s->bandwidth_limit = d; + s->bandwidth_limit = value; qemu_file_set_rate_limit(s->file, s->bandwidth_limit); - - return 0; } -int do_migrate_set_downtime(Monitor *mon, const QDict *qdict, - QObject **ret_data) +void qmp_migrate_set_downtime(double value, Error **errp) { - double d; - - d = qdict_get_double(qdict, "value") * 1e9; - d = MAX(0, MIN(UINT64_MAX, d)); - max_downtime = (uint64_t)d; - - return 0; + value *= 1e9; + value = MAX(0, MIN(UINT64_MAX, value)); + max_downtime = (uint64_t)value; } diff --git a/migration.h b/migration.h index 78a50d31ac..372b066b48 100644 --- a/migration.h +++ b/migration.h @@ -42,15 +42,8 @@ int qemu_start_incoming_migration(const char *uri); int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data); -int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data); - -int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data); - uint64_t migrate_max_downtime(void); -int do_migrate_set_downtime(Monitor *mon, const QDict *qdict, - QObject **ret_data); - void do_info_migrate_print(Monitor *mon, const QObject *data); void do_info_migrate(Monitor *mon, QObject **ret_data); @@ -513,10 +513,10 @@ static int do_qmp_capabilities(Monitor *mon, const QDict *params, static void handle_user_command(Monitor *mon, const char *cmdline); -static int do_hmp_passthrough(Monitor *mon, const QDict *params, - QObject **ret_data) +char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, + int64_t cpu_index, Error **errp) { - int ret = 0; + char *output = NULL; Monitor *old_mon, hmp; CharDriverState mchar; @@ -527,25 +527,30 @@ static int do_hmp_passthrough(Monitor *mon, const QDict *params, old_mon = cur_mon; cur_mon = &hmp; - if (qdict_haskey(params, "cpu-index")) { - ret = monitor_set_cpu(qdict_get_int(params, "cpu-index")); + if (has_cpu_index) { + int ret = monitor_set_cpu(cpu_index); if (ret < 0) { cur_mon = old_mon; - qerror_report(QERR_INVALID_PARAMETER_VALUE, "cpu-index", "a CPU number"); + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); goto out; } } - handle_user_command(&hmp, qdict_get_str(params, "command-line")); + handle_user_command(&hmp, command_line); cur_mon = old_mon; if (qemu_chr_mem_osize(hmp.chr) > 0) { - *ret_data = QOBJECT(qemu_chr_mem_to_qs(hmp.chr)); + QString *str = qemu_chr_mem_to_qs(hmp.chr); + output = g_strdup(qstring_get_str(str)); + QDECREF(str); + } else { + output = g_strdup(""); } out: qemu_chr_close_mem(hmp.chr); - return ret; + return output; } static int compare_cmd(const char *name, const char *list) @@ -1073,65 +1078,6 @@ static void do_singlestep(Monitor *mon, const QDict *qdict) } } -static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs); - -struct bdrv_iterate_context { - Monitor *mon; - int err; -}; - -static void iostatus_bdrv_it(void *opaque, BlockDriverState *bs) -{ - bdrv_iostatus_reset(bs); -} - -/** - * do_cont(): Resume emulation. - */ -static int do_cont(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - struct bdrv_iterate_context context = { mon, 0 }; - - if (runstate_check(RUN_STATE_INMIGRATE)) { - qerror_report(QERR_MIGRATION_EXPECTED); - return -1; - } else if (runstate_check(RUN_STATE_INTERNAL_ERROR) || - runstate_check(RUN_STATE_SHUTDOWN)) { - qerror_report(QERR_RESET_REQUIRED); - return -1; - } - - bdrv_iterate(iostatus_bdrv_it, NULL); - bdrv_iterate(encrypted_bdrv_it, &context); - /* only resume the vm if all keys are set and valid */ - if (!context.err) { - vm_start(); - return 0; - } else { - return -1; - } -} - -static void bdrv_key_cb(void *opaque, int err) -{ - Monitor *mon = opaque; - - /* another key was set successfully, retry to continue */ - if (!err) - do_cont(mon, NULL, NULL); -} - -static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs) -{ - struct bdrv_iterate_context *context = opaque; - - if (!context->err && bdrv_key_required(bs)) { - context->err = -EBUSY; - monitor_read_bdrv_key_start(context->mon, bs, bdrv_key_cb, - context->mon); - } -} - static void do_gdbserver(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_try_str(qdict, "device"); @@ -1370,81 +1316,6 @@ static void do_print(Monitor *mon, const QDict *qdict) monitor_printf(mon, "\n"); } -static int do_memory_save(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - FILE *f; - uint32_t size = qdict_get_int(qdict, "size"); - const char *filename = qdict_get_str(qdict, "filename"); - target_long addr = qdict_get_int(qdict, "val"); - uint32_t l; - CPUState *env; - uint8_t buf[1024]; - int ret = -1; - - env = mon_get_cpu(); - - f = fopen(filename, "wb"); - if (!f) { - qerror_report(QERR_OPEN_FILE_FAILED, filename); - return -1; - } - while (size != 0) { - l = sizeof(buf); - if (l > size) - l = size; - cpu_memory_rw_debug(env, addr, buf, l, 0); - if (fwrite(buf, 1, l, f) != l) { - monitor_printf(mon, "fwrite() error in do_memory_save\n"); - goto exit; - } - addr += l; - size -= l; - } - - ret = 0; - -exit: - fclose(f); - return ret; -} - -static int do_physical_memory_save(Monitor *mon, const QDict *qdict, - QObject **ret_data) -{ - FILE *f; - uint32_t l; - uint8_t buf[1024]; - uint32_t size = qdict_get_int(qdict, "size"); - const char *filename = qdict_get_str(qdict, "filename"); - target_phys_addr_t addr = qdict_get_int(qdict, "val"); - int ret = -1; - - f = fopen(filename, "wb"); - if (!f) { - qerror_report(QERR_OPEN_FILE_FAILED, filename); - return -1; - } - while (size != 0) { - l = sizeof(buf); - if (l > size) - l = size; - cpu_physical_memory_read(addr, buf, l); - if (fwrite(buf, 1, l, f) != l) { - monitor_printf(mon, "fwrite() error in do_physical_memory_save\n"); - goto exit; - } - fflush(f); - addr += l; - size -= l; - } - - ret = 0; - -exit: - fclose(f); - return ret; -} - static void do_sum(Monitor *mon, const QDict *qdict) { uint32_t addr; @@ -1796,16 +1667,6 @@ static void do_boot_set(Monitor *mon, const QDict *qdict) } } -/** - * do_system_powerdown(): Issue a machine powerdown - */ -static int do_system_powerdown(Monitor *mon, const QDict *qdict, - QObject **ret_data) -{ - qemu_system_powerdown_request(); - return 0; -} - #if defined(TARGET_I386) static void print_pte(Monitor *mon, target_phys_addr_t addr, target_phys_addr_t pte, @@ -2348,25 +2209,6 @@ static void do_wav_capture(Monitor *mon, const QDict *qdict) } #endif -#if defined(TARGET_I386) -static int do_inject_nmi(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - CPUState *env; - - for (env = first_cpu; env != NULL; env = env->next_cpu) { - cpu_interrupt(env, CPU_INTERRUPT_NMI); - } - - return 0; -} -#else -static int do_inject_nmi(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - qerror_report(QERR_UNSUPPORTED); - return -1; -} -#endif - static qemu_acl *find_acl(Monitor *mon, const char *name) { qemu_acl *acl = qemu_acl_find(name); @@ -4943,3 +4785,18 @@ int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs, return err; } + +int monitor_read_block_device_key(Monitor *mon, const char *device, + BlockDriverCompletionFunc *completion_cb, + void *opaque) +{ + BlockDriverState *bs; + + bs = bdrv_find(device); + if (!bs) { + monitor_printf(mon, "Device not found %s\n", device); + return -1; + } + + return monitor_read_bdrv_key_start(mon, bs, completion_cb, opaque); +} @@ -49,6 +49,9 @@ void monitor_resume(Monitor *mon); int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs, BlockDriverCompletionFunc *completion_cb, void *opaque); +int monitor_read_block_device_key(Monitor *mon, const char *device, + BlockDriverCompletionFunc *completion_cb, + void *opaque); int monitor_get_fd(Monitor *mon, const char *fdname); @@ -34,6 +34,7 @@ #include "monitor.h" #include "qemu-common.h" #include "qemu_socket.h" +#include "qmp-commands.h" #include "hw/qdev.h" #include "iov.h" @@ -1258,12 +1259,10 @@ void do_info_network(Monitor *mon) } } -int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data) +void qmp_set_link(const char *name, bool up, Error **errp) { VLANState *vlan; VLANClientState *vc = NULL; - const char *name = qdict_get_str(qdict, "name"); - int up = qdict_get_bool(qdict, "up"); QTAILQ_FOREACH(vlan, &vlans, next) { QTAILQ_FOREACH(vc, &vlan->clients, next) { @@ -1280,8 +1279,8 @@ int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data) done: if (!vc) { - qerror_report(QERR_DEVICE_NOT_FOUND, name); - return -1; + error_set(errp, QERR_DEVICE_NOT_FOUND, name); + return; } vc->link_down = !up; @@ -1300,7 +1299,6 @@ done: if (vc->peer && vc->peer->info->link_status_changed) { vc->peer->info->link_status_changed(vc->peer); } - return 0; } void net_cleanup(void) @@ -122,7 +122,6 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models, const char *default_model); void do_info_network(Monitor *mon); -int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data); /* NIC info */ diff --git a/net/socket.c b/net/socket.c index 0f091645ed..aaf9be48e2 100644 --- a/net/socket.c +++ b/net/socket.c @@ -161,10 +161,11 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr #endif if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) { - fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n", - inet_ntoa(mcastaddr->sin_addr), + fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) " + "does not contain a multicast address\n", + inet_ntoa(mcastaddr->sin_addr), (int)ntohl(mcastaddr->sin_addr.s_addr)); - return -1; + return -1; } fd = qemu_socket(PF_INET, SOCK_DGRAM, 0); @@ -177,8 +178,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); if (ret < 0) { - perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); - goto fail; + perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); + goto fail; } ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr)); @@ -198,8 +199,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(struct ip_mreq)); if (ret < 0) { - perror("setsockopt(IP_ADD_MEMBERSHIP)"); - goto fail; + perror("setsockopt(IP_ADD_MEMBERSHIP)"); + goto fail; } /* Force mcast msgs to loopback (eg. several QEMUs in same host */ @@ -207,8 +208,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const char *)&loop, sizeof(loop)); if (ret < 0) { - perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)"); - goto fail; + perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)"); + goto fail; } /* If a bind address is given, only send packets from that address */ @@ -260,37 +261,37 @@ static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, */ if (is_connected) { - if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) { - /* must be bound */ - if (saddr.sin_addr.s_addr==0) { - fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n", - fd); - return NULL; - } - /* clone dgram socket */ - newfd = net_socket_mcast_create(&saddr, NULL); - if (newfd < 0) { - /* error already reported by net_socket_mcast_create() */ - close(fd); - return NULL; - } - /* clone newfd to fd, close newfd */ - dup2(newfd, fd); - close(newfd); - - } else { - fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n", - fd, strerror(errno)); - return NULL; - } + if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) { + /* must be bound */ + if (saddr.sin_addr.s_addr == 0) { + fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, " + "cannot setup multicast dst addr\n", fd); + goto err; + } + /* clone dgram socket */ + newfd = net_socket_mcast_create(&saddr, NULL); + if (newfd < 0) { + /* error already reported by net_socket_mcast_create() */ + goto err; + } + /* clone newfd to fd, close newfd */ + dup2(newfd, fd); + close(newfd); + + } else { + fprintf(stderr, + "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n", + fd, strerror(errno)); + goto err; + } } nc = qemu_new_net_client(&net_dgram_socket_info, vlan, NULL, model, name); snprintf(nc->info_str, sizeof(nc->info_str), - "socket: fd=%d (%s mcast=%s:%d)", - fd, is_connected ? "cloned" : "", - inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + "socket: fd=%d (%s mcast=%s:%d)", + fd, is_connected ? "cloned" : "", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); s = DO_UPCAST(NetSocketState, nc, nc); @@ -302,6 +303,10 @@ static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, if (is_connected) s->dgram_dst=saddr; return s; + +err: + closesocket(fd); + return NULL; } static void net_socket_connect(void *opaque) @@ -349,8 +354,10 @@ static NetSocketState *net_socket_fd_init(VLANState *vlan, if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type, (socklen_t *)&optlen)< 0) { - fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n", fd); - return NULL; + fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n", + fd); + closesocket(fd); + return NULL; } switch(so_type) { case SOCK_DGRAM: @@ -383,9 +390,7 @@ static void net_socket_accept(void *opaque) } } s1 = net_socket_fd_init(s->vlan, s->model, s->name, fd, 1); - if (!s1) { - closesocket(fd); - } else { + if (s1) { snprintf(s1->nc.info_str, sizeof(s1->nc.info_str), "socket: connection from %s:%d", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); @@ -512,7 +517,7 @@ static int net_socket_mcast_init(VLANState *vlan, fd = net_socket_mcast_create(&saddr, param_localaddr); if (fd < 0) - return -1; + return -1; s = net_socket_fd_init(vlan, model, name, fd, 0); if (!s) @@ -549,7 +554,6 @@ int net_init_socket(QemuOpts *opts, } if (!net_socket_fd_init(vlan, "socket", name, fd, 1)) { - close(fd); return -1; } } else if (qemu_opt_get(opts, "listen")) { diff --git a/qapi-schema-guest.json b/qapi-schema-guest.json index f4bcd1a532..5f8a18d4d8 100644 --- a/qapi-schema-guest.json +++ b/qapi-schema-guest.json @@ -43,7 +43,11 @@ # # Since: 0.15.0 ## -{ 'type': 'GuestAgentInfo', 'data': {'version': 'str'} } +{ 'type': 'GuestAgentCommandInfo', + 'data': { 'name': 'str', 'enabled': 'bool' } } +{ 'type': 'GuestAgentInfo', + 'data': { 'version': 'str', + 'supported_commands': ['GuestAgentCommandInfo'] } } { 'command': 'guest-info', 'returns': 'GuestAgentInfo' } diff --git a/qapi-schema-test.json b/qapi-schema-test.json index 3acedad7ee..2b38919001 100644 --- a/qapi-schema-test.json +++ b/qapi-schema-test.json @@ -16,6 +16,12 @@ 'dict': { 'userdef': 'UserDefOne', 'string': 'str' }, '*dict2': { 'userdef': 'UserDefOne', 'string': 'str' } } } } +{ 'type': 'UserDefNested', + 'data': { 'string0': 'str', + 'dict1': { 'string1': 'str', + 'dict2': { 'userdef1': 'UserDefOne', 'string2': 'str' }, + '*dict3': { 'userdef2': 'UserDefOne', 'string3': 'str' } } } } + # testing commands { 'command': 'user_def_cmd', 'data': {} } { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} } diff --git a/qapi-schema.json b/qapi-schema.json index fbbdbe0914..f358b490b0 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -901,3 +901,270 @@ # Notes: Do not use this command. ## { 'command': 'cpu', 'data': {'index': 'int'} } + +## +# @memsave: +# +# Save a portion of guest memory to a file. +# +# @val: the virtual address of the guest to start from +# +# @size: the size of memory region to save +# +# @filename: the file to save the memory to as binary data +# +# @cpu-index: #optional the index of the virtual CPU to use for translating the +# virtual address (defaults to CPU 0) +# +# Returns: Nothing on success +# If @cpu is not a valid VCPU, InvalidParameterValue +# If @filename cannot be opened, OpenFileFailed +# If an I/O error occurs while writing the file, IOError +# +# Since: 0.14.0 +# +# Notes: Errors were not reliably returned until 1.1 +## +{ 'command': 'memsave', + 'data': {'val': 'int', 'size': 'int', 'filename': 'str', '*cpu-index': 'int'} } + +## +# @pmemsave: +# +# Save a portion of guest physical memory to a file. +# +# @val: the physical address of the guest to start from +# +# @size: the size of memory region to save +# +# @filename: the file to save the memory to as binary data +# +# Returns: Nothing on success +# If @filename cannot be opened, OpenFileFailed +# If an I/O error occurs while writing the file, IOError +# +# Since: 0.14.0 +# +# Notes: Errors were not reliably returned until 1.1 +## +{ 'command': 'pmemsave', + 'data': {'val': 'int', 'size': 'int', 'filename': 'str'} } + +## +# @cont: +# +# Resume guest VCPU execution. +# +# Since: 0.14.0 +# +# Returns: If successful, nothing +# If the QEMU is waiting for an incoming migration, MigrationExpected +# If QEMU was started with an encrypted block device and a key has +# not yet been set, DeviceEncrypted. +# +# Notes: This command will succeed if the guest is currently running. +## +{ 'command': 'cont' } + +## +# @inject-nmi: +# +# Injects an Non-Maskable Interrupt into all guest's VCPUs. +# +# Returns: If successful, nothing +# If the Virtual Machine doesn't support NMI injection, Unsupported +# +# Since: 0.14.0 +# +# Notes: Only x86 Virtual Machines support this command. +## +{ 'command': 'inject-nmi' } + +## +# @set_link: +# +# Sets the link status of a virtual network adapter. +# +# @name: the device name of the virtual network adapter +# +# @up: true to set the link status to be up +# +# Returns: Nothing on success +# If @name is not a valid network device, DeviceNotFound +# +# Since: 0.14.0 +# +# Notes: Not all network adapters support setting link status. This command +# will succeed even if the network adapter does not support link status +# notification. +## +{ 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} } + +## +# @block_passwd: +# +# This command sets the password of a block device that has not been open +# with a password and requires one. +# +# The two cases where this can happen are a block device is created through +# QEMU's initial command line or a block device is changed through the legacy +# @change interface. +# +# In the event that the block device is created through the initial command +# line, the VM will start in the stopped state regardless of whether '-S' is +# used. The intention is for a management tool to query the block devices to +# determine which ones are encrypted, set the passwords with this command, and +# then start the guest with the @cont command. +# +# @device: the name of the device to set the password on +# +# @password: the password to use for the device +# +# Returns: nothing on success +# If @device is not a valid block device, DeviceNotFound +# If @device is not encrypted, DeviceNotEncrypted +# If @password is not valid for this device, InvalidPassword +# +# Notes: Not all block formats support encryption and some that do are not +# able to validate that a password is correct. Disk corruption may +# occur if an invalid password is specified. +# +# Since: 0.14.0 +## +{ 'command': 'block_passwd', 'data': {'device': 'str', 'password': 'str'} } + +## +# @balloon: +# +# Request the balloon driver to change its balloon size. +# +# @value: the target size of the balloon in bytes +# +# Returns: Nothing on success +# If the balloon driver is enabled but not functional because the KVM +# kernel module cannot support it, KvmMissingCap +# If no balloon device is present, DeviceNotActive +# +# Notes: This command just issues a request to the guest. When it returns, +# the balloon size may not have changed. A guest can change the balloon +# size independent of this command. +# +# Since: 0.14.0 +## +{ 'command': 'balloon', 'data': {'value': 'int'} } + +## +# @block_resize +# +# Resize a block image while a guest is running. +# +# @device: the name of the device to get the image resized +# +# @size: new image size in bytes +# +# Returns: nothing on success +# If @device is not a valid block device, DeviceNotFound +# +# Notes: This command returns UndefinedError in a number of error conditions. +# +# Since: 0.14.0 +## +{ 'command': 'block_resize', 'data': { 'device': 'str', 'size': 'int' }} + +## +# @blockdev-snapshot-sync +# +# Generates a synchronous snapshot of a block device. +# +# @device: the name of the device to generate the snapshot from. +# +# @snapshot-file: the target of the new image. If the file exists, or if it +# is a device, the snapshot will be created in the existing +# file/device. If does not exist, a new file will be created. +# +# @format: #optional the format of the snapshot image, default is 'qcow2'. +# +# Returns: nothing on success +# If @device is not a valid block device, DeviceNotFound +# If @snapshot-file can't be opened, OpenFileFailed +# If @format is invalid, InvalidBlockFormat +# +# Notes: One of the last steps taken by this command is to close the current +# image being used by @device and open the @snapshot-file one. If that +# fails, the command will try to reopen the original image file. If +# that also fails OpenFileFailed will be returned and the guest may get +# unexpected errors. +# +# Since 0.14.0 +## +{ 'command': 'blockdev-snapshot-sync', + 'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str' } } + +## +# @human-monitor-command: +# +# Execute a command on the human monitor and return the output. +# +# @command-line: the command to execute in the human monitor +# +# @cpu-index: #optional The CPU to use for commands that require an implicit CPU +# +# Returns: the output of the command as a string +# +# Since: 0.14.0 +# +# Notes: This command only exists as a stop-gap. It's use is highly +# discouraged. The semantics of this command are not guaranteed. +# +# Known limitations: +# +# o This command is stateless, this means that commands that depend +# on state information (such as getfd) might not work +# +# o Commands that prompt the user for data (eg. 'cont' when the block +# device is encrypted) don't currently work +## +{ 'command': 'human-monitor-command', + 'data': {'command-line': 'str', '*cpu-index': 'int'}, + 'returns': 'str' } + +## +# @migrate_cancel +# +# Cancel the current executing migration process. +# +# Returns: nothing on success +# +# Notes: This command succeeds even if there is no migration process running. +# +# Since: 0.14.0 +## +{ 'command': 'migrate_cancel' } + +## +# @migrate_set_downtime +# +# Set maximum tolerated downtime for migration. +# +# @value: maximum downtime in seconds +# +# Returns: nothing on success +# +# Since: 0.14.0 +## +{ 'command': 'migrate_set_downtime', 'data': {'value': 'number'} } + +## +# @migrate_set_speed +# +# Set maximum speed for migration. +# +# @value: maximum speed in bytes. +# +# Returns: nothing on success +# +# Notes: A value lesser than zero will be automatically round up to zero. +# +# Since: 0.14.0 +## +{ 'command': 'migrate_set_speed', 'data': {'value': 'int'} } diff --git a/qapi/qmp-core.h b/qapi/qmp-core.h index f1c26e4b2e..3bb3acb589 100644 --- a/qapi/qmp-core.h +++ b/qapi/qmp-core.h @@ -31,11 +31,15 @@ typedef struct QmpCommand QmpCommandType type; QmpCommandFunc *fn; QTAILQ_ENTRY(QmpCommand) node; + bool enabled; } QmpCommand; void qmp_register_command(const char *name, QmpCommandFunc *fn); QmpCommand *qmp_find_command(const char *name); QObject *qmp_dispatch(QObject *request); +void qmp_disable_command(const char *name); +bool qmp_command_is_enabled(const char *name); +char **qmp_get_command_list(void); #endif diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 558469325c..43f640a95e 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -79,6 +79,10 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp) error_set(errp, QERR_COMMAND_NOT_FOUND, command); return NULL; } + if (!cmd->enabled) { + error_set(errp, QERR_COMMAND_DISABLED, command); + return NULL; + } if (!qdict_haskey(dict, "arguments")) { args = qdict_new(); diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index 5ff99cff14..25c89ad098 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -14,7 +14,7 @@ #include "qapi/qmp-core.h" -static QTAILQ_HEAD(, QmpCommand) qmp_commands = +static QTAILQ_HEAD(QmpCommandList, QmpCommand) qmp_commands = QTAILQ_HEAD_INITIALIZER(qmp_commands); void qmp_register_command(const char *name, QmpCommandFunc *fn) @@ -24,17 +24,63 @@ void qmp_register_command(const char *name, QmpCommandFunc *fn) cmd->name = name; cmd->type = QCT_NORMAL; cmd->fn = fn; + cmd->enabled = true; QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node); } QmpCommand *qmp_find_command(const char *name) { - QmpCommand *i; + QmpCommand *cmd; - QTAILQ_FOREACH(i, &qmp_commands, node) { - if (strcmp(i->name, name) == 0) { - return i; + QTAILQ_FOREACH(cmd, &qmp_commands, node) { + if (strcmp(cmd->name, name) == 0) { + return cmd; } } return NULL; } + +void qmp_disable_command(const char *name) +{ + QmpCommand *cmd; + + QTAILQ_FOREACH(cmd, &qmp_commands, node) { + if (strcmp(cmd->name, name) == 0) { + cmd->enabled = false; + return; + } + } +} + +bool qmp_command_is_enabled(const char *name) +{ + QmpCommand *cmd; + + QTAILQ_FOREACH(cmd, &qmp_commands, node) { + if (strcmp(cmd->name, name) == 0) { + return cmd->enabled; + } + } + + return false; +} + +char **qmp_get_command_list(void) +{ + QmpCommand *cmd; + int count = 1; + char **list_head, **list; + + QTAILQ_FOREACH(cmd, &qmp_commands, node) { + count++; + } + + list_head = list = g_malloc0(count * sizeof(char *)); + + QTAILQ_FOREACH(cmd, &qmp_commands, node) { + *list = strdup(cmd->name); + list++; + } + + return list_head; +} @@ -27,6 +27,7 @@ #include "signal.h" #include "qerror.h" #include "error_int.h" +#include "qapi/qmp-core.h" #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" #define QGA_PIDFILE_DEFAULT "/var/run/qemu-ga.pid" @@ -91,6 +92,8 @@ static void usage(const char *cmd) " -v, --verbose log extra debugging information\n" " -V, --version print version information and exit\n" " -d, --daemonize become a daemon\n" +" -b, --blacklist comma-seperated list of RPCs to disable (no spaces, \"?\"" +" to list available RPCs)\n" " -h, --help display this help and exit\n" "\n" "Report bugs to <mdroth@linux.vnet.ibm.com>\n" @@ -548,7 +551,7 @@ static void init_guest_agent(GAState *s) int main(int argc, char **argv) { - const char *sopt = "hVvdm:p:l:f:"; + const char *sopt = "hVvdm:p:l:f:b:"; const char *method = NULL, *path = NULL, *pidfile = QGA_PIDFILE_DEFAULT; const struct option lopt[] = { { "help", 0, NULL, 'h' }, @@ -559,13 +562,16 @@ int main(int argc, char **argv) { "method", 0, NULL, 'm' }, { "path", 0, NULL, 'p' }, { "daemonize", 0, NULL, 'd' }, + { "blacklist", 0, NULL, 'b' }, { NULL, 0, NULL, 0 } }; - int opt_ind = 0, ch, daemonize = 0; + int opt_ind = 0, ch, daemonize = 0, i, j, len; GLogLevelFlags log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; FILE *log_file = stderr; GAState *s; + module_call_init(MODULE_INIT_QAPI); + while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { case 'm': @@ -595,6 +601,32 @@ int main(int argc, char **argv) case 'd': daemonize = 1; break; + case 'b': { + char **list_head, **list; + if (*optarg == '?') { + list_head = list = qmp_get_command_list(); + while (*list != NULL) { + printf("%s\n", *list); + g_free(*list); + list++; + } + g_free(list_head); + return 0; + } + for (j = 0, i = 0, len = strlen(optarg); i < len; i++) { + if (optarg[i] == ',') { + optarg[i] = 0; + qmp_disable_command(&optarg[j]); + g_debug("disabling command: %s", &optarg[j]); + j = i + 1; + } + } + if (j < i) { + qmp_disable_command(&optarg[j]); + g_debug("disabling command: %s", &optarg[j]); + } + break; + } case 'h': usage(argv[0]); return 0; @@ -624,7 +656,6 @@ int main(int argc, char **argv) ga_command_state_init_all(s->command_state); ga_state = s; - module_call_init(MODULE_INIT_QAPI); init_guest_agent(ga_state); register_signal_handlers(); diff --git a/qemu-thread-posix.c b/qemu-thread-posix.c index ac3c0c9d14..9e1b5fbdaa 100644 --- a/qemu-thread-posix.c +++ b/qemu-thread-posix.c @@ -117,20 +117,33 @@ void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex) void qemu_thread_create(QemuThread *thread, void *(*start_routine)(void*), - void *arg) + void *arg, int mode) { + sigset_t set, oldset; int err; + pthread_attr_t attr; - /* Leave signal handling to the iothread. */ - sigset_t set, oldset; + err = pthread_attr_init(&attr); + if (err) { + error_exit(err, __func__); + } + if (mode == QEMU_THREAD_DETACHED) { + err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (err) { + error_exit(err, __func__); + } + } + /* Leave signal handling to the iothread. */ sigfillset(&set); pthread_sigmask(SIG_SETMASK, &set, &oldset); - err = pthread_create(&thread->thread, NULL, start_routine, arg); + err = pthread_create(&thread->thread, &attr, start_routine, arg); if (err) error_exit(err, __func__); pthread_sigmask(SIG_SETMASK, &oldset, NULL); + + pthread_attr_destroy(&attr); } void qemu_thread_get_self(QemuThread *thread) @@ -147,3 +160,15 @@ void qemu_thread_exit(void *retval) { pthread_exit(retval); } + +void *qemu_thread_join(QemuThread *thread) +{ + int err; + void *ret; + + err = pthread_join(thread->thread, &ret); + if (err) { + error_exit(err, __func__); + } + return ret; +} diff --git a/qemu-thread-win32.c b/qemu-thread-win32.c index db8e744729..a13ffcca69 100644 --- a/qemu-thread-win32.c +++ b/qemu-thread-win32.c @@ -193,41 +193,78 @@ void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex) } struct QemuThreadData { - QemuThread *thread; - void *(*start_routine)(void *); - void *arg; + /* Passed to win32_start_routine. */ + void *(*start_routine)(void *); + void *arg; + short mode; + + /* Only used for joinable threads. */ + bool exited; + void *ret; + CRITICAL_SECTION cs; }; static int qemu_thread_tls_index = TLS_OUT_OF_INDEXES; static unsigned __stdcall win32_start_routine(void *arg) { - struct QemuThreadData data = *(struct QemuThreadData *) arg; - QemuThread *thread = data.thread; - - free(arg); - TlsSetValue(qemu_thread_tls_index, thread); - - /* - * Use DuplicateHandle instead of assigning thread->thread in the - * creating thread to avoid races. It's simpler this way than with - * synchronization. - */ - DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), - GetCurrentProcess(), &thread->thread, - 0, FALSE, DUPLICATE_SAME_ACCESS); - - qemu_thread_exit(data.start_routine(data.arg)); + QemuThreadData *data = (QemuThreadData *) arg; + void *(*start_routine)(void *) = data->start_routine; + void *thread_arg = data->arg; + + if (data->mode == QEMU_THREAD_DETACHED) { + g_free(data); + data = NULL; + } else { + InitializeCriticalSection(&data->cs); + } + TlsSetValue(qemu_thread_tls_index, data); + qemu_thread_exit(start_routine(thread_arg)); abort(); } void qemu_thread_exit(void *arg) { - QemuThread *thread = TlsGetValue(qemu_thread_tls_index); - thread->ret = arg; - CloseHandle(thread->thread); - thread->thread = NULL; - ExitThread(0); + QemuThreadData *data = TlsGetValue(qemu_thread_tls_index); + if (data) { + data->ret = arg; + EnterCriticalSection(&data->cs); + data->exited = true; + LeaveCriticalSection(&data->cs); + } + _endthreadex(0); +} + +void *qemu_thread_join(QemuThread *thread) +{ + QemuThreadData *data; + void *ret; + HANDLE handle; + + data = thread->data; + if (!data) { + return NULL; + } + /* + * Because multiple copies of the QemuThread can exist via + * qemu_thread_get_self, we need to store a value that cannot + * leak there. The simplest, non racy way is to store the TID, + * discard the handle that _beginthreadex gives back, and + * get another copy of the handle here. + */ + EnterCriticalSection(&data->cs); + if (!data->exited) { + handle = OpenThread(SYNCHRONIZE, FALSE, thread->tid); + LeaveCriticalSection(&data->cs); + WaitForSingleObject(handle, INFINITE); + CloseHandle(handle); + } else { + LeaveCriticalSection(&data->cs); + } + ret = data->ret; + DeleteCriticalSection(&data->cs); + g_free(data); + return ret; } static inline void qemu_thread_init(void) @@ -243,39 +280,35 @@ static inline void qemu_thread_init(void) void qemu_thread_create(QemuThread *thread, void *(*start_routine)(void *), - void *arg) + void *arg, int mode) { HANDLE hThread; struct QemuThreadData *data; qemu_thread_init(); data = g_malloc(sizeof *data); - data->thread = thread; data->start_routine = start_routine; data->arg = arg; + data->mode = mode; + data->exited = false; hThread = (HANDLE) _beginthreadex(NULL, 0, win32_start_routine, - data, 0, NULL); + data, 0, &thread->tid); if (!hThread) { error_exit(GetLastError(), __func__); } CloseHandle(hThread); + thread->data = (mode == QEMU_THREAD_DETACHED) ? NULL : data; } void qemu_thread_get_self(QemuThread *thread) { - if (!thread->thread) { - /* In the main thread of the process. Initialize the QemuThread - pointer in TLS, and use the dummy GetCurrentThread handle as - the identifier for qemu_thread_is_self. */ - qemu_thread_init(); - TlsSetValue(qemu_thread_tls_index, thread); - thread->thread = GetCurrentThread(); - } + qemu_thread_init(); + thread->data = TlsGetValue(qemu_thread_tls_index); + thread->tid = GetCurrentThreadId(); } int qemu_thread_is_self(QemuThread *thread) { - QemuThread *this_thread = TlsGetValue(qemu_thread_tls_index); - return this_thread->thread == thread->thread; + return GetCurrentThreadId() == thread->tid; } diff --git a/qemu-thread-win32.h b/qemu-thread-win32.h index 878f86a910..2983490a58 100644 --- a/qemu-thread-win32.h +++ b/qemu-thread-win32.h @@ -13,9 +13,10 @@ struct QemuCond { HANDLE continue_event; }; +typedef struct QemuThreadData QemuThreadData; struct QemuThread { - HANDLE thread; - void *ret; + QemuThreadData *data; + unsigned tid; }; #endif diff --git a/qemu-thread.h b/qemu-thread.h index e008b60028..a78a8f2524 100644 --- a/qemu-thread.h +++ b/qemu-thread.h @@ -13,6 +13,9 @@ typedef struct QemuThread QemuThread; #include "qemu-thread-posix.h" #endif +#define QEMU_THREAD_JOINABLE 0 +#define QEMU_THREAD_DETACHED 1 + void qemu_mutex_init(QemuMutex *mutex); void qemu_mutex_destroy(QemuMutex *mutex); void qemu_mutex_lock(QemuMutex *mutex); @@ -35,8 +38,9 @@ void qemu_cond_broadcast(QemuCond *cond); void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex); void qemu_thread_create(QemuThread *thread, - void *(*start_routine)(void*), - void *arg); + void *(*start_routine)(void *), + void *arg, int mode); +void *qemu_thread_join(QemuThread *thread); void qemu_thread_get_self(QemuThread *thread); int qemu_thread_is_self(QemuThread *thread); void qemu_thread_exit(void *retval); @@ -65,6 +65,10 @@ static const QErrorStringTable qerror_table[] = { .desc = "The command %(name) has not been found", }, { + .error_fmt = QERR_COMMAND_DISABLED, + .desc = "The command %(name) has been disabled for this instance", + }, + { .error_fmt = QERR_DEVICE_ENCRYPTED, .desc = "Device '%(device)' is encrypted", }, @@ -149,6 +153,10 @@ static const QErrorStringTable qerror_table[] = { .desc = "Password incorrect", }, { + .error_fmt = QERR_IO_ERROR, + .desc = "An IO error has occurred", + }, + { .error_fmt = QERR_JSON_PARSING, .desc = "Invalid JSON syntax", }, @@ -66,6 +66,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_COMMAND_NOT_FOUND \ "{ 'class': 'CommandNotFound', 'data': { 'name': %s } }" +#define QERR_COMMAND_DISABLED \ + "{ 'class': 'CommandDisabled', 'data': { 'name': %s } }" + #define QERR_DEVICE_ENCRYPTED \ "{ 'class': 'DeviceEncrypted', 'data': { 'device': %s } }" @@ -126,6 +129,9 @@ QError *qobject_to_qerror(const QObject *obj); #define QERR_INVALID_PASSWORD \ "{ 'class': 'InvalidPassword', 'data': {} }" +#define QERR_IO_ERROR \ + "{ 'class': 'IOError', 'data': {} }" + #define QERR_JSON_PARSING \ "{ 'class': 'JSONParsing', 'data': {} }" diff --git a/qga/guest-agent-commands.c b/qga/guest-agent-commands.c index 6da9904819..a09c8ca230 100644 --- a/qga/guest-agent-commands.c +++ b/qga/guest-agent-commands.c @@ -57,9 +57,33 @@ void qmp_guest_ping(Error **err) struct GuestAgentInfo *qmp_guest_info(Error **err) { GuestAgentInfo *info = g_malloc0(sizeof(GuestAgentInfo)); + GuestAgentCommandInfo *cmd_info; + GuestAgentCommandInfoList *cmd_info_list; + char **cmd_list_head, **cmd_list; info->version = g_strdup(QGA_VERSION); + cmd_list_head = cmd_list = qmp_get_command_list(); + if (*cmd_list_head == NULL) { + goto out; + } + + while (*cmd_list) { + cmd_info = g_malloc0(sizeof(GuestAgentCommandInfo)); + cmd_info->name = strdup(*cmd_list); + cmd_info->enabled = qmp_command_is_enabled(cmd_info->name); + + cmd_info_list = g_malloc0(sizeof(GuestAgentCommandInfoList)); + cmd_info_list->value = cmd_info; + cmd_info_list->next = info->supported_commands; + info->supported_commands = cmd_info_list; + + g_free(*cmd_list); + cmd_list++; + } + +out: + g_free(cmd_list_head); return info; } diff --git a/qmp-commands.hx b/qmp-commands.hx index 94da2a8ef2..002e7e8bd9 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -199,10 +199,7 @@ EQMP { .name = "cont", .args_type = "", - .params = "", - .help = "resume emulation", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_cont, + .mhandler.cmd_new = qmp_marshal_input_cont, }, SQMP @@ -244,10 +241,7 @@ EQMP { .name = "system_powerdown", .args_type = "", - .params = "", - .help = "send system power down event", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_system_powerdown, + .mhandler.cmd_new = qmp_marshal_input_system_powerdown, }, SQMP @@ -355,11 +349,8 @@ EQMP { .name = "memsave", - .args_type = "val:l,size:i,filename:s", - .params = "addr size file", - .help = "save to disk virtual memory dump starting at 'addr' of size 'size'", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_memory_save, + .args_type = "val:l,size:i,filename:s,cpu:i?", + .mhandler.cmd_new = qmp_marshal_input_memsave, }, SQMP @@ -373,6 +364,7 @@ Arguments: - "val": the starting address (json-int) - "size": the memory size, in bytes (json-int) - "filename": file path (json-string) +- "cpu": virtual CPU index (json-int, optional) Example: @@ -382,17 +374,12 @@ Example: "filename": "/tmp/virtual-mem-dump" } } <- { "return": {} } -Note: Depends on the current CPU. - EQMP { .name = "pmemsave", .args_type = "val:l,size:i,filename:s", - .params = "addr size file", - .help = "save to disk physical memory dump starting at 'addr' of size 'size'", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_physical_memory_save, + .mhandler.cmd_new = qmp_marshal_input_pmemsave, }, SQMP @@ -420,10 +407,7 @@ EQMP { .name = "inject-nmi", .args_type = "", - .params = "", - .help = "", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_inject_nmi, + .mhandler.cmd_new = qmp_marshal_input_inject_nmi, }, SQMP @@ -487,10 +471,7 @@ EQMP { .name = "migrate_cancel", .args_type = "", - .params = "", - .help = "cancel the current VM migration", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_migrate_cancel, + .mhandler.cmd_new = qmp_marshal_input_migrate_cancel, }, SQMP @@ -511,10 +492,7 @@ EQMP { .name = "migrate_set_speed", .args_type = "value:o", - .params = "value", - .help = "set maximum speed (in bytes) for migrations", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_migrate_set_speed, + .mhandler.cmd_new = qmp_marshal_input_migrate_set_speed, }, SQMP @@ -537,10 +515,7 @@ EQMP { .name = "migrate_set_downtime", .args_type = "value:T", - .params = "value", - .help = "set maximum tolerated downtime (in seconds) for migrations", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_migrate_set_downtime, + .mhandler.cmd_new = qmp_marshal_input_migrate_set_downtime, }, SQMP @@ -658,10 +633,7 @@ EQMP { .name = "block_resize", .args_type = "device:B,size:o", - .params = "device size", - .help = "resize a block image", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_block_resize, + .mhandler.cmd_new = qmp_marshal_input_block_resize, }, SQMP @@ -684,10 +656,8 @@ EQMP { .name = "blockdev-snapshot-sync", - .args_type = "device:B,snapshot-file:s?,format:s?", - .params = "device [new-image-file] [format]", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_snapshot_blkdev, + .args_type = "device:B,snapshot-file:s,format:s?", + .mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_sync, }, SQMP @@ -719,11 +689,7 @@ EQMP { .name = "balloon", .args_type = "value:M", - .params = "target", - .help = "request VM to change its memory allocation (in MB)", - .user_print = monitor_user_noop, - .mhandler.cmd_async = do_balloon, - .flags = MONITOR_CMD_ASYNC, + .mhandler.cmd_new = qmp_marshal_input_balloon, }, SQMP @@ -746,10 +712,7 @@ EQMP { .name = "set_link", .args_type = "name:s,up:b", - .params = "name on|off", - .help = "change the link status of a network adapter", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_set_link, + .mhandler.cmd_new = qmp_marshal_input_set_link, }, SQMP @@ -825,10 +788,7 @@ EQMP { .name = "block_passwd", .args_type = "device:B,password:s", - .params = "block_passwd device password", - .help = "set the password of encrypted block devices", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_block_set_passwd, + .mhandler.cmd_new = qmp_marshal_input_block_passwd, }, SQMP @@ -1001,10 +961,7 @@ EQMP { .name = "human-monitor-command", .args_type = "command-line:s,cpu-index:i?", - .params = "", - .help = "", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_hmp_passthrough, + .mhandler.cmd_new = qmp_marshal_input_human_monitor_command, }, SQMP @@ -117,3 +117,40 @@ SpiceInfo *qmp_query_spice(Error **errp) return NULL; }; #endif + +static void iostatus_bdrv_it(void *opaque, BlockDriverState *bs) +{ + bdrv_iostatus_reset(bs); +} + +static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs) +{ + Error **err = opaque; + + if (!error_is_set(err) && bdrv_key_required(bs)) { + error_set(err, QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs)); + } +} + +void qmp_cont(Error **errp) +{ + Error *local_err = NULL; + + if (runstate_check(RUN_STATE_INMIGRATE)) { + error_set(errp, QERR_MIGRATION_EXPECTED); + return; + } else if (runstate_check(RUN_STATE_INTERNAL_ERROR) || + runstate_check(RUN_STATE_SHUTDOWN)) { + error_set(errp, QERR_RESET_REQUIRED); + return; + } + + bdrv_iterate(iostatus_bdrv_it, NULL); + bdrv_iterate(encrypted_bdrv_it, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + vm_start(); +} @@ -235,6 +235,9 @@ static int stdio_pclose(void *opaque) QEMUFileStdio *s = opaque; int ret; ret = pclose(s->stdio_file); + if (ret == -1) { + ret = -errno; + } g_free(s); return ret; } @@ -242,9 +245,12 @@ static int stdio_pclose(void *opaque) static int stdio_fclose(void *opaque) { QEMUFileStdio *s = opaque; - fclose(s->stdio_file); + int ret = 0; + if (fclose(s->stdio_file) == EOF) { + ret = -errno; + } g_free(s); - return 0; + return ret; } QEMUFile *qemu_popen(FILE *stdio_file, const char *mode) @@ -436,6 +442,22 @@ void qemu_file_set_error(QEMUFile *f, int ret) f->last_error = ret; } +/** Sets last_error conditionally + * + * Sets last_error only if ret is negative _and_ no error + * was set before. + */ +static void qemu_file_set_if_error(QEMUFile *f, int ret) +{ + if (ret < 0 && !f->last_error) { + qemu_file_set_error(f, ret); + } +} + +/** Flushes QEMUFile buffer + * + * In case of error, last_error is set. + */ void qemu_fflush(QEMUFile *f) { if (!f->put_buffer) @@ -448,7 +470,7 @@ void qemu_fflush(QEMUFile *f) if (len > 0) f->buf_offset += f->buf_index; else - f->last_error = -EINVAL; + qemu_file_set_error(f, -EINVAL); f->buf_index = 0; } } @@ -479,15 +501,44 @@ static void qemu_fill_buffer(QEMUFile *f) } else if (len == 0) { f->last_error = -EIO; } else if (len != -EAGAIN) - f->last_error = len; + qemu_file_set_error(f, len); } -int qemu_fclose(QEMUFile *f) +/** Calls close function and set last_error if needed + * + * Internal function. qemu_fflush() must be called before this. + * + * Returns f->close() return value, or 0 if close function is not set. + */ +static int qemu_close(QEMUFile *f) { int ret = 0; - qemu_fflush(f); - if (f->close) + if (f->close) { ret = f->close(f->opaque); + qemu_file_set_if_error(f, ret); + } + return ret; +} + +/** Closes the file + * + * Returns negative error value if any error happened on previous operations or + * while closing the file. Returns 0 or positive number on success. + * + * The meaning of return value on success depends on the specific backend + * being used. + */ +int qemu_fclose(QEMUFile *f) +{ + int ret; + qemu_fflush(f); + ret = qemu_close(f); + /* If any error was spotted before closing, we should report it + * instead of the close() return value. + */ + if (f->last_error) { + ret = f->last_error; + } g_free(f); return ret; } diff --git a/target-cris/cpu.h b/target-cris/cpu.h index 8ae0ce3ef3..453afbb66e 100644 --- a/target-cris/cpu.h +++ b/target-cris/cpu.h @@ -67,6 +67,8 @@ #define Q_FLAG 0x80000000 #define M_FLAG 0x40000000 #define PFIX_FLAG 0x800 /* CRISv10 Only. */ +#define F_FLAG_V10 0x400 +#define P_FLAG_V10 0x200 #define S_FLAG 0x200 #define R_FLAG 0x100 #define P_FLAG 0x80 diff --git a/target-cris/helper.c b/target-cris/helper.c index 75f0035e6e..5bc6d810cb 100644 --- a/target-cris/helper.c +++ b/target-cris/helper.c @@ -157,6 +157,7 @@ static void do_interruptv10(CPUState *env) /* Now that we are in kernel mode, load the handlers address. */ env->pc = ldl_code(env->pregs[PR_EBP] + ex_vec * 4); env->locked_irq = 1; + env->pregs[PR_CCS] |= F_FLAG_V10; /* set F. */ qemu_log_mask(CPU_LOG_INT, "%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", __func__, env->pc, ex_vec, diff --git a/target-cris/translate_v10.c b/target-cris/translate_v10.c index 637ac2084a..95053b64fb 100644 --- a/target-cris/translate_v10.c +++ b/target-cris/translate_v10.c @@ -62,6 +62,65 @@ static inline void cris_illegal_insn(DisasContext *dc) t_gen_raise_exception(EXCP_BREAK); } +static void gen_store_v10_conditional(DisasContext *dc, TCGv addr, TCGv val, + unsigned int size, int mem_index) +{ + int l1 = gen_new_label(); + TCGv taddr = tcg_temp_local_new(); + TCGv tval = tcg_temp_local_new(); + TCGv t1 = tcg_temp_local_new(); + dc->postinc = 0; + cris_evaluate_flags(dc); + + tcg_gen_mov_tl(taddr, addr); + tcg_gen_mov_tl(tval, val); + + /* Store only if F flag isn't set */ + tcg_gen_andi_tl(t1, cpu_PR[PR_CCS], F_FLAG_V10); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + if (size == 1) { + tcg_gen_qemu_st8(tval, taddr, mem_index); + } else if (size == 2) { + tcg_gen_qemu_st16(tval, taddr, mem_index); + } else { + tcg_gen_qemu_st32(tval, taddr, mem_index); + } + gen_set_label(l1); + tcg_gen_shri_tl(t1, t1, 1); /* shift F to P position */ + tcg_gen_or_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], t1); /*P=F*/ + tcg_temp_free(t1); + tcg_temp_free(tval); + tcg_temp_free(taddr); +} + +static void gen_store_v10(DisasContext *dc, TCGv addr, TCGv val, + unsigned int size) +{ + int mem_index = cpu_mmu_index(dc->env); + + /* If we get a fault on a delayslot we must keep the jmp state in + the cpu-state to be able to re-execute the jmp. */ + if (dc->delayed_branch == 1) { + cris_store_direct_jmp(dc); + } + + /* Conditional writes. We only support the kind were X is known + at translation time. */ + if (dc->flagx_known && dc->flags_x) { + gen_store_v10_conditional(dc, addr, val, size, mem_index); + return; + } + + if (size == 1) { + tcg_gen_qemu_st8(val, addr, mem_index); + } else if (size == 2) { + tcg_gen_qemu_st16(val, addr, mem_index); + } else { + tcg_gen_qemu_st32(val, addr, mem_index); + } +} + + /* Prefix flag and register are used to handle the more complex addressing modes. */ static void cris_set_prefix(DisasContext *dc) @@ -313,7 +372,8 @@ static unsigned int dec10_setclrf(DisasContext *dc) if (set) { tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], flags); } else { - tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~flags); + tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], + ~(flags|F_FLAG_V10|P_FLAG_V10)); } dc->flags_uptodate = 1; @@ -723,7 +783,7 @@ static unsigned int dec10_ind_move_r_m(DisasContext *dc, unsigned int size) LOG_DIS("move.%d $r%d, [$r%d]\n", dc->size, dc->src, dc->dst); addr = tcg_temp_new(); crisv10_prepare_memaddr(dc, addr, size); - gen_store(dc, addr, cpu_R[dc->dst], size); + gen_store_v10(dc, addr, cpu_R[dc->dst], size); insn_len += crisv10_post_memaddr(dc, size); return insn_len; @@ -767,10 +827,10 @@ static unsigned int dec10_ind_move_pr_m(DisasContext *dc) t0 = tcg_temp_new(); cris_evaluate_flags(dc); tcg_gen_andi_tl(t0, cpu_PR[PR_CCS], ~PFIX_FLAG); - gen_store(dc, addr, t0, size); + gen_store_v10(dc, addr, t0, size); tcg_temp_free(t0); } else { - gen_store(dc, addr, cpu_PR[dc->dst], size); + gen_store_v10(dc, addr, cpu_PR[dc->dst], size); } t0 = tcg_temp_new(); insn_len += crisv10_post_memaddr(dc, size); @@ -793,9 +853,9 @@ static void dec10_movem_r_m(DisasContext *dc) tcg_gen_mov_tl(t0, addr); for (i = dc->dst; i >= 0; i--) { if ((pfix && dc->mode == CRISV10_MODE_AUTOINC) && dc->src == i) { - gen_store(dc, addr, t0, 4); + gen_store_v10(dc, addr, t0, 4); } else { - gen_store(dc, addr, cpu_R[i], 4); + gen_store_v10(dc, addr, cpu_R[i], 4); } tcg_gen_addi_tl(addr, addr, 4); } diff --git a/target-i386/translate.c b/target-i386/translate.c index 1ef8d16ac7..8321bf39a5 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -4870,20 +4870,23 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) tcg_gen_sub_tl(t2, cpu_regs[R_EAX], t0); gen_extu(ot, t2); tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, label1); + label2 = gen_new_label(); if (mod == 3) { - label2 = gen_new_label(); gen_op_mov_reg_v(ot, R_EAX, t0); tcg_gen_br(label2); gen_set_label(label1); gen_op_mov_reg_v(ot, rm, t1); - gen_set_label(label2); } else { - tcg_gen_mov_tl(t1, t0); + /* perform no-op store cycle like physical cpu; must be + before changing accumulator to ensure idempotency if + the store faults and the instruction is restarted */ + gen_op_st_v(ot + s->mem_index, t0, a0); gen_op_mov_reg_v(ot, R_EAX, t0); + tcg_gen_br(label2); gen_set_label(label1); - /* always store */ gen_op_st_v(ot + s->mem_index, t1, a0); } + gen_set_label(label2); tcg_gen_mov_tl(cpu_cc_src, t0); tcg_gen_mov_tl(cpu_cc_dst, t2); s->cc_op = CC_OP_SUBB + ot; diff --git a/test-qmp-input-visitor.c b/test-qmp-input-visitor.c new file mode 100644 index 0000000000..1f3ab031f7 --- /dev/null +++ b/test-qmp-input-visitor.c @@ -0,0 +1,270 @@ +/* + * QMP Input Visitor unit-tests. + * + * Copyright (C) 2011 Red Hat Inc. + * + * Authors: + * Luiz Capitulino <lcapitulino@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> +#include <stdarg.h> + +#include "qapi/qmp-input-visitor.h" +#include "test-qapi-types.h" +#include "test-qapi-visit.h" +#include "qemu-objects.h" + +typedef struct TestInputVisitorData { + QObject *obj; + QmpInputVisitor *qiv; +} TestInputVisitorData; + +static void visitor_input_teardown(TestInputVisitorData *data, + const void *unused) +{ + qobject_decref(data->obj); + data->obj = NULL; + + if (data->qiv) { + qmp_input_visitor_cleanup(data->qiv); + data->qiv = NULL; + } +} + +/* This is provided instead of a test setup function so that the JSON + string used by the tests are kept in the test functions (and not + int main()) */ +static Visitor *visitor_input_test_init(TestInputVisitorData *data, + const char *json_string, ...) +{ + Visitor *v; + va_list ap; + + va_start(ap, json_string); + data->obj = qobject_from_jsonv(json_string, &ap); + va_end(ap); + + g_assert(data->obj != NULL); + + data->qiv = qmp_input_visitor_new(data->obj); + g_assert(data->qiv != NULL); + + v = qmp_input_get_visitor(data->qiv); + g_assert(v != NULL); + + return v; +} + +static void test_visitor_in_int(TestInputVisitorData *data, + const void *unused) +{ + int64_t res = 0, value = -42; + Error *errp = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "%d", value); + + visit_type_int(v, &res, NULL, &errp); + g_assert(!error_is_set(&errp)); + g_assert_cmpint(res, ==, value); +} + +static void test_visitor_in_bool(TestInputVisitorData *data, + const void *unused) +{ + Error *errp = NULL; + bool res = false; + Visitor *v; + + v = visitor_input_test_init(data, "true"); + + visit_type_bool(v, &res, NULL, &errp); + g_assert(!error_is_set(&errp)); + g_assert_cmpint(res, ==, true); +} + +static void test_visitor_in_number(TestInputVisitorData *data, + const void *unused) +{ + double res = 0, value = 3.14; + Error *errp = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "%f", value); + + visit_type_number(v, &res, NULL, &errp); + g_assert(!error_is_set(&errp)); + g_assert_cmpfloat(res, ==, value); +} + +static void test_visitor_in_string(TestInputVisitorData *data, + const void *unused) +{ + char *res = NULL, *value = (char *) "Q E M U"; + Error *errp = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "%s", value); + + visit_type_str(v, &res, NULL, &errp); + g_assert(!error_is_set(&errp)); + g_assert_cmpstr(res, ==, value); + + g_free(res); +} + +static void test_visitor_in_enum(TestInputVisitorData *data, + const void *unused) +{ + Error *errp = NULL; + Visitor *v; + EnumOne i; + + for (i = 0; EnumOne_lookup[i]; i++) { + EnumOne res = -1; + + v = visitor_input_test_init(data, "%s", EnumOne_lookup[i]); + + visit_type_EnumOne(v, &res, NULL, &errp); + g_assert(!error_is_set(&errp)); + g_assert_cmpint(i, ==, res); + + visitor_input_teardown(data, NULL); + } + + data->obj = NULL; + data->qiv = NULL; +} + +typedef struct TestStruct +{ + int64_t integer; + bool boolean; + char *string; +} TestStruct; + +static void visit_type_TestStruct(Visitor *v, TestStruct **obj, + const char *name, Error **errp) +{ + visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), + errp); + + visit_type_int(v, &(*obj)->integer, "integer", errp); + visit_type_bool(v, &(*obj)->boolean, "boolean", errp); + visit_type_str(v, &(*obj)->string, "string", errp); + + visit_end_struct(v, errp); +} + +static void test_visitor_in_struct(TestInputVisitorData *data, + const void *unused) +{ + TestStruct *p = NULL; + Error *errp = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); + + visit_type_TestStruct(v, &p, NULL, &errp); + g_assert(!error_is_set(&errp)); + g_assert_cmpint(p->integer, ==, -42); + g_assert(p->boolean == true); + g_assert_cmpstr(p->string, ==, "foo"); + + g_free(p->string); + g_free(p); +} + +static void check_and_free_str(char *str, const char *cmp) +{ + g_assert_cmpstr(str, ==, cmp); + g_free(str); +} + +static void test_visitor_in_struct_nested(TestInputVisitorData *data, + const void *unused) +{ + UserDefNested *udp = NULL; + Error *errp = NULL; + Visitor *v; + + v = visitor_input_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string' }, 'string2': 'string2'}}}"); + + visit_type_UserDefNested(v, &udp, NULL, &errp); + g_assert(!error_is_set(&errp)); + + check_and_free_str(udp->string0, "string0"); + check_and_free_str(udp->dict1.string1, "string1"); + g_assert_cmpint(udp->dict1.dict2.userdef1->integer, ==, 42); + check_and_free_str(udp->dict1.dict2.userdef1->string, "string"); + check_and_free_str(udp->dict1.dict2.string2, "string2"); + g_assert(udp->dict1.has_dict3 == false); + + g_free(udp->dict1.dict2.userdef1); + g_free(udp); +} + +static void test_visitor_in_list(TestInputVisitorData *data, + const void *unused) +{ + UserDefOneList *item, *head = NULL; + Error *errp = NULL; + Visitor *v; + int i; + + v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]"); + + visit_type_UserDefOneList(v, &head, NULL, &errp); + g_assert(!error_is_set(&errp)); + g_assert(head != NULL); + + for (i = 0, item = head; item; item = item->next, i++) { + char string[12]; + + snprintf(string, sizeof(string), "string%d", i); + g_assert_cmpstr(item->value->string, ==, string); + g_assert_cmpint(item->value->integer, ==, 42 + i); + } + + qapi_free_UserDefOneList(head); +} + +static void input_visitor_test_add(const char *testpath, + TestInputVisitorData *data, + void (*test_func)(TestInputVisitorData *data, const void *user_data)) +{ + g_test_add(testpath, TestInputVisitorData, data, NULL, test_func, + visitor_input_teardown); +} + +int main(int argc, char **argv) +{ + TestInputVisitorData in_visitor_data; + + g_test_init(&argc, &argv, NULL); + + input_visitor_test_add("/visitor/input/int", + &in_visitor_data, test_visitor_in_int); + input_visitor_test_add("/visitor/input/bool", + &in_visitor_data, test_visitor_in_bool); + input_visitor_test_add("/visitor/input/number", + &in_visitor_data, test_visitor_in_number); + input_visitor_test_add("/visitor/input/string", + &in_visitor_data, test_visitor_in_string); + input_visitor_test_add("/visitor/input/enum", + &in_visitor_data, test_visitor_in_enum); + input_visitor_test_add("/visitor/input/struct", + &in_visitor_data, test_visitor_in_struct); + input_visitor_test_add("/visitor/input/struct-nested", + &in_visitor_data, test_visitor_in_struct_nested); + input_visitor_test_add("/visitor/input/list", + &in_visitor_data, test_visitor_in_list); + + g_test_run(); + + return 0; +} diff --git a/test-qmp-output-visitor.c b/test-qmp-output-visitor.c new file mode 100644 index 0000000000..c94c208125 --- /dev/null +++ b/test-qmp-output-visitor.c @@ -0,0 +1,423 @@ +/* + * QMP Output Visitor unit-tests. + * + * Copyright (C) 2011 Red Hat Inc. + * + * Authors: + * Luiz Capitulino <lcapitulino@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <glib.h> + +#include "qapi/qmp-output-visitor.h" +#include "test-qapi-types.h" +#include "test-qapi-visit.h" +#include "qemu-objects.h" + +typedef struct TestOutputVisitorData { + QmpOutputVisitor *qov; + Visitor *ov; +} TestOutputVisitorData; + +static void visitor_output_setup(TestOutputVisitorData *data, + const void *unused) +{ + data->qov = qmp_output_visitor_new(); + g_assert(data->qov != NULL); + + data->ov = qmp_output_get_visitor(data->qov); + g_assert(data->ov != NULL); +} + +static void visitor_output_teardown(TestOutputVisitorData *data, + const void *unused) +{ + qmp_output_visitor_cleanup(data->qov); + data->qov = NULL; + data->ov = NULL; +} + +static void test_visitor_out_int(TestOutputVisitorData *data, + const void *unused) +{ + int64_t value = -42; + Error *errp = NULL; + QObject *obj; + + visit_type_int(data->ov, &value, NULL, &errp); + g_assert(error_is_set(&errp) == 0); + + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QINT); + g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, value); + + qobject_decref(obj); +} + +static void test_visitor_out_bool(TestOutputVisitorData *data, + const void *unused) +{ + Error *errp = NULL; + bool value = true; + QObject *obj; + + visit_type_bool(data->ov, &value, NULL, &errp); + g_assert(error_is_set(&errp) == 0); + + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QBOOL); + g_assert(qbool_get_int(qobject_to_qbool(obj)) == value); + + qobject_decref(obj); +} + +static void test_visitor_out_number(TestOutputVisitorData *data, + const void *unused) +{ + double value = 3.14; + Error *errp = NULL; + QObject *obj; + + visit_type_number(data->ov, &value, NULL, &errp); + g_assert(error_is_set(&errp) == 0); + + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QFLOAT); + g_assert(qfloat_get_double(qobject_to_qfloat(obj)) == value); + + qobject_decref(obj); +} + +static void test_visitor_out_string(TestOutputVisitorData *data, + const void *unused) +{ + char *string = (char *) "Q E M U"; + Error *errp = NULL; + QObject *obj; + + visit_type_str(data->ov, &string, NULL, &errp); + g_assert(error_is_set(&errp) == 0); + + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QSTRING); + g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, string); + + qobject_decref(obj); +} + +static void test_visitor_out_no_string(TestOutputVisitorData *data, + const void *unused) +{ + char *string = NULL; + Error *errp = NULL; + QObject *obj; + + /* A null string should return "" */ + visit_type_str(data->ov, &string, NULL, &errp); + g_assert(error_is_set(&errp) == 0); + + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QSTRING); + g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, ""); + + qobject_decref(obj); +} + +static void test_visitor_out_enum(TestOutputVisitorData *data, + const void *unused) +{ + Error *errp = NULL; + QObject *obj; + EnumOne i; + + for (i = 0; i < ENUM_ONE_MAX; i++) { + visit_type_EnumOne(data->ov, &i, "unused", &errp); + g_assert(!error_is_set(&errp)); + + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QSTRING); + g_assert_cmpstr(qstring_get_str(qobject_to_qstring(obj)), ==, + EnumOne_lookup[i]); + qobject_decref(obj); + } +} + +static void test_visitor_out_enum_errors(TestOutputVisitorData *data, + const void *unused) +{ + EnumOne i, bad_values[] = { ENUM_ONE_MAX, -1 }; + Error *errp; + + for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) { + errp = NULL; + visit_type_EnumOne(data->ov, &bad_values[i], "unused", &errp); + g_assert(error_is_set(&errp) == true); + error_free(errp); + } +} + +typedef struct TestStruct +{ + int64_t integer; + bool boolean; + char *string; +} TestStruct; + +static void visit_type_TestStruct(Visitor *v, TestStruct **obj, + const char *name, Error **errp) +{ + visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), + errp); + + visit_type_int(v, &(*obj)->integer, "integer", errp); + visit_type_bool(v, &(*obj)->boolean, "boolean", errp); + visit_type_str(v, &(*obj)->string, "string", errp); + + visit_end_struct(v, errp); +} + +static void test_visitor_out_struct(TestOutputVisitorData *data, + const void *unused) +{ + TestStruct test_struct = { .integer = 42, + .boolean = false, + .string = (char *) "foo"}; + TestStruct *p = &test_struct; + Error *errp = NULL; + QObject *obj; + QDict *qdict; + + visit_type_TestStruct(data->ov, &p, NULL, &errp); + g_assert(!error_is_set(&errp)); + + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QDICT); + + qdict = qobject_to_qdict(obj); + g_assert_cmpint(qdict_size(qdict), ==, 3); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, 42); + g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, 0); + g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, "foo"); + + QDECREF(qdict); +} + +static void test_visitor_out_struct_nested(TestOutputVisitorData *data, + const void *unused) +{ + int64_t value = 42; + Error *errp = NULL; + UserDefNested *ud2; + QObject *obj; + QDict *qdict, *dict1, *dict2, *dict3, *userdef; + const char *string = "user def string"; + const char *strings[] = { "fourty two", "fourty three", "fourty four", + "fourty five" }; + + ud2 = g_malloc0(sizeof(*ud2)); + ud2->string0 = g_strdup(strings[0]); + + ud2->dict1.string1 = g_strdup(strings[1]); + ud2->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne)); + ud2->dict1.dict2.userdef1->string = g_strdup(string); + ud2->dict1.dict2.userdef1->integer = value; + ud2->dict1.dict2.string2 = g_strdup(strings[2]); + + ud2->dict1.has_dict3 = true; + ud2->dict1.dict3.userdef2 = g_malloc0(sizeof(UserDefOne)); + ud2->dict1.dict3.userdef2->string = g_strdup(string); + ud2->dict1.dict3.userdef2->integer = value; + ud2->dict1.dict3.string3 = g_strdup(strings[3]); + + visit_type_UserDefNested(data->ov, &ud2, "unused", &errp); + g_assert(!error_is_set(&errp)); + + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QDICT); + + qdict = qobject_to_qdict(obj); + g_assert_cmpint(qdict_size(qdict), ==, 2); + g_assert_cmpstr(qdict_get_str(qdict, "string0"), ==, strings[0]); + + dict1 = qdict_get_qdict(qdict, "dict1"); + g_assert_cmpint(qdict_size(dict1), ==, 3); + g_assert_cmpstr(qdict_get_str(dict1, "string1"), ==, strings[1]); + + dict2 = qdict_get_qdict(dict1, "dict2"); + g_assert_cmpint(qdict_size(dict2), ==, 2); + g_assert_cmpstr(qdict_get_str(dict2, "string2"), ==, strings[2]); + userdef = qdict_get_qdict(dict2, "userdef1"); + g_assert_cmpint(qdict_size(userdef), ==, 2); + g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value); + g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string); + + dict3 = qdict_get_qdict(dict1, "dict3"); + g_assert_cmpint(qdict_size(dict3), ==, 2); + g_assert_cmpstr(qdict_get_str(dict3, "string3"), ==, strings[3]); + userdef = qdict_get_qdict(dict3, "userdef2"); + g_assert_cmpint(qdict_size(userdef), ==, 2); + g_assert_cmpint(qdict_get_int(userdef, "integer"), ==, value); + g_assert_cmpstr(qdict_get_str(userdef, "string"), ==, string); + + QDECREF(qdict); + qapi_free_UserDefNested(ud2); +} + +typedef struct TestStructList +{ + TestStruct *value; + struct TestStructList *next; +} TestStructList; + +static void visit_type_TestStructList(Visitor *v, TestStructList **obj, + const char *name, Error **errp) +{ + GenericList *i, **head = (GenericList **)obj; + + visit_start_list(v, name, errp); + + for (*head = i = visit_next_list(v, head, errp); i; i = visit_next_list(v, &i, errp)) { + TestStructList *native_i = (TestStructList *)i; + visit_type_TestStruct(v, &native_i->value, NULL, errp); + } + + visit_end_list(v, errp); +} + +static void test_visitor_out_list(TestOutputVisitorData *data, + const void *unused) +{ + char *value_str = (char *) "list value"; + TestStructList *p, *head = NULL; + const int max_items = 10; + bool value_bool = true; + int value_int = 10; + Error *errp = NULL; + QListEntry *entry; + QObject *obj; + QList *qlist; + int i; + + for (i = 0; i < max_items; i++) { + p = g_malloc0(sizeof(*p)); + p->value = g_malloc0(sizeof(*p->value)); + p->value->integer = value_int; + p->value->boolean = value_bool; + p->value->string = value_str; + + p->next = head; + head = p; + } + + visit_type_TestStructList(data->ov, &head, NULL, &errp); + g_assert(!error_is_set(&errp)); + + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QLIST); + + qlist = qobject_to_qlist(obj); + g_assert(!qlist_empty(qlist)); + + i = 0; + QLIST_FOREACH_ENTRY(qlist, entry) { + QDict *qdict; + + g_assert(qobject_type(entry->value) == QTYPE_QDICT); + qdict = qobject_to_qdict(entry->value); + g_assert_cmpint(qdict_size(qdict), ==, 3); + g_assert_cmpint(qdict_get_int(qdict, "integer"), ==, value_int); + g_assert_cmpint(qdict_get_bool(qdict, "boolean"), ==, value_bool); + g_assert_cmpstr(qdict_get_str(qdict, "string"), ==, value_str); + i++; + } + g_assert_cmpint(i, ==, max_items); + + QDECREF(qlist); + + for (p = head; p;) { + TestStructList *tmp = p->next; + g_free(p->value); + g_free(p); + p = tmp; + } +} + +static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data, + const void *unused) +{ + UserDefNestedList *p, *head = NULL; + const char string[] = "foo bar"; + int i, max_count = 1024; + + for (i = 0; i < max_count; i++) { + p = g_malloc0(sizeof(*p)); + p->value = g_malloc0(sizeof(*p->value)); + + p->value->string0 = g_strdup(string); + p->value->dict1.string1 = g_strdup(string); + p->value->dict1.dict2.userdef1 = g_malloc0(sizeof(UserDefOne)); + p->value->dict1.dict2.userdef1->string = g_strdup(string); + p->value->dict1.dict2.userdef1->integer = 42; + p->value->dict1.dict2.string2 = g_strdup(string); + p->value->dict1.has_dict3 = false; + + p->next = head; + head = p; + } + + qapi_free_UserDefNestedList(head); +} + +static void output_visitor_test_add(const char *testpath, + TestOutputVisitorData *data, + void (*test_func)(TestOutputVisitorData *data, const void *user_data)) +{ + g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup, + test_func, visitor_output_teardown); +} + +int main(int argc, char **argv) +{ + TestOutputVisitorData out_visitor_data; + + g_test_init(&argc, &argv, NULL); + + output_visitor_test_add("/visitor/output/int", + &out_visitor_data, test_visitor_out_int); + output_visitor_test_add("/visitor/output/bool", + &out_visitor_data, test_visitor_out_bool); + output_visitor_test_add("/visitor/output/number", + &out_visitor_data, test_visitor_out_number); + output_visitor_test_add("/visitor/output/string", + &out_visitor_data, test_visitor_out_string); + output_visitor_test_add("/visitor/output/no-string", + &out_visitor_data, test_visitor_out_no_string); + output_visitor_test_add("/visitor/output/enum", + &out_visitor_data, test_visitor_out_enum); + output_visitor_test_add("/visitor/output/enum-errors", + &out_visitor_data, test_visitor_out_enum_errors); + output_visitor_test_add("/visitor/output/struct", + &out_visitor_data, test_visitor_out_struct); + output_visitor_test_add("/visitor/output/struct-nested", + &out_visitor_data, test_visitor_out_struct_nested); + output_visitor_test_add("/visitor/output/list", + &out_visitor_data, test_visitor_out_list); + output_visitor_test_add("/visitor/output/list-qapi-free", + &out_visitor_data, test_visitor_out_list_qapi_free); + + g_test_run(); + + return 0; +} diff --git a/test-visitor.c b/test-visitor.c deleted file mode 100644 index f90b711883..0000000000 --- a/test-visitor.c +++ /dev/null @@ -1,338 +0,0 @@ -#include <glib.h> -#include "qapi/qmp-output-visitor.h" -#include "qapi/qmp-input-visitor.h" -#include "test-qapi-types.h" -#include "test-qapi-visit.h" -#include "qemu-objects.h" - -typedef struct TestStruct -{ - int64_t x; - int64_t y; -} TestStruct; - -typedef struct TestStructList -{ - TestStruct *value; - struct TestStructList *next; -} TestStructList; - -static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp) -{ - visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct), errp); - visit_type_int(v, &(*obj)->x, "x", errp); - visit_type_int(v, &(*obj)->y, "y", errp); - visit_end_struct(v, errp); -} - -static void visit_type_TestStructList(Visitor *m, TestStructList ** obj, const char *name, Error **errp) -{ - GenericList *i, **head = (GenericList **)obj; - - visit_start_list(m, name, errp); - - for (*head = i = visit_next_list(m, head, errp); i; i = visit_next_list(m, &i, errp)) { - TestStructList *native_i = (TestStructList *)i; - visit_type_TestStruct(m, &native_i->value, NULL, errp); - } - - visit_end_list(m, errp); -} - -/* test core visitor methods */ -static void test_visitor_core(void) -{ - QmpOutputVisitor *mo; - QmpInputVisitor *mi; - Visitor *v; - TestStruct ts = { 42, 82 }; - TestStruct *pts = &ts; - TestStructList *lts = NULL; - Error *err = NULL; - QObject *obj; - QList *qlist; - QDict *qdict; - QString *str; - int64_t value = 0; - - mo = qmp_output_visitor_new(); - v = qmp_output_get_visitor(mo); - - visit_type_TestStruct(v, &pts, NULL, &err); - - obj = qmp_output_get_qobject(mo); - - str = qobject_to_json(obj); - - printf("%s\n", qstring_get_str(str)); - - QDECREF(str); - - obj = QOBJECT(qint_from_int(0x42)); - - mi = qmp_input_visitor_new(obj); - v = qmp_input_get_visitor(mi); - - visit_type_int(v, &value, NULL, &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - - g_assert(value == 0x42); - - qobject_decref(obj); - - obj = qobject_from_json("{'x': 42, 'y': 84}"); - mi = qmp_input_visitor_new(obj); - v = qmp_input_get_visitor(mi); - - pts = NULL; - - visit_type_TestStruct(v, &pts, NULL, &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - - g_assert(pts != NULL); - g_assert(pts->x == 42); - g_assert(pts->y == 84); - - qobject_decref(obj); - g_free(pts); - - /* test list input visitor */ - obj = qobject_from_json("[{'x': 42, 'y': 84}, {'x': 12, 'y': 24}]"); - mi = qmp_input_visitor_new(obj); - v = qmp_input_get_visitor(mi); - - visit_type_TestStructList(v, <s, NULL, &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - - g_assert(lts != NULL); - g_assert(lts->value->x == 42); - g_assert(lts->value->y == 84); - - g_assert(lts->next != NULL); - g_assert(lts->next->value->x == 12); - g_assert(lts->next->value->y == 24); - g_assert(lts->next->next == NULL); - - qobject_decref(obj); - - /* test list output visitor */ - mo = qmp_output_visitor_new(); - v = qmp_output_get_visitor(mo); - visit_type_TestStructList(v, <s, NULL, &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - obj = qmp_output_get_qobject(mo); - g_print("obj: %s\n", qstring_get_str(qobject_to_json(obj))); - - qlist = qobject_to_qlist(obj); - assert(qlist); - obj = qlist_pop(qlist); - qdict = qobject_to_qdict(obj); - assert(qdict); - assert(qdict_get_int(qdict, "x") == 42); - assert(qdict_get_int(qdict, "y") == 84); - qobject_decref(obj); - - obj = qlist_pop(qlist); - qdict = qobject_to_qdict(obj); - assert(qdict); - assert(qdict_get_int(qdict, "x") == 12); - assert(qdict_get_int(qdict, "y") == 24); - qobject_decref(obj); - - qmp_output_visitor_cleanup(mo); - QDECREF(qlist); -} - -/* test deep nesting with refs to other user-defined types */ -static void test_nested_structs(void) -{ - QmpOutputVisitor *mo; - QmpInputVisitor *mi; - Visitor *v; - UserDefOne ud1; - UserDefOne *ud1_p = &ud1, *ud1c_p = NULL; - UserDefTwo ud2; - UserDefTwo *ud2_p = &ud2, *ud2c_p = NULL; - Error *err = NULL; - QObject *obj; - QString *str; - - ud1.integer = 42; - ud1.string = strdup("forty two"); - - /* sanity check */ - mo = qmp_output_visitor_new(); - v = qmp_output_get_visitor(mo); - visit_type_UserDefOne(v, &ud1_p, "o_O", &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - obj = qmp_output_get_qobject(mo); - g_assert(obj); - qobject_decref(obj); - - ud2.string = strdup("forty three"); - ud2.dict.string = strdup("forty four"); - ud2.dict.dict.userdef = ud1_p; - ud2.dict.dict.string = strdup("forty five"); - ud2.dict.has_dict2 = true; - ud2.dict.dict2.userdef = ud1_p; - ud2.dict.dict2.string = strdup("forty six"); - - /* c type -> qobject */ - mo = qmp_output_visitor_new(); - v = qmp_output_get_visitor(mo); - visit_type_UserDefTwo(v, &ud2_p, "unused", &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - obj = qmp_output_get_qobject(mo); - g_assert(obj); - str = qobject_to_json_pretty(obj); - g_print("%s\n", qstring_get_str(str)); - QDECREF(str); - - /* qobject -> c type, should match original struct */ - mi = qmp_input_visitor_new(obj); - v = qmp_input_get_visitor(mi); - visit_type_UserDefTwo(v, &ud2c_p, NULL, &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - - g_assert(!g_strcmp0(ud2c_p->string, ud2.string)); - g_assert(!g_strcmp0(ud2c_p->dict.string, ud2.dict.string)); - - ud1c_p = ud2c_p->dict.dict.userdef; - g_assert(ud1c_p->integer == ud1_p->integer); - g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string)); - - g_assert(!g_strcmp0(ud2c_p->dict.dict.string, ud2.dict.dict.string)); - - ud1c_p = ud2c_p->dict.dict2.userdef; - g_assert(ud1c_p->integer == ud1_p->integer); - g_assert(!g_strcmp0(ud1c_p->string, ud1_p->string)); - - g_assert(!g_strcmp0(ud2c_p->dict.dict2.string, ud2.dict.dict2.string)); - g_free(ud1.string); - g_free(ud2.string); - g_free(ud2.dict.string); - g_free(ud2.dict.dict.string); - g_free(ud2.dict.dict2.string); - - qapi_free_UserDefTwo(ud2c_p); - - qobject_decref(obj); -} - -/* test enum values */ -static void test_enums(void) -{ - QmpOutputVisitor *mo; - QmpInputVisitor *mi; - Visitor *v; - EnumOne enum1 = ENUM_ONE_VALUE2, enum1_cpy = ENUM_ONE_VALUE1; - Error *err = NULL; - QObject *obj; - QString *str; - - /* C type -> QObject */ - mo = qmp_output_visitor_new(); - v = qmp_output_get_visitor(mo); - visit_type_EnumOne(v, &enum1, "unused", &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - obj = qmp_output_get_qobject(mo); - g_assert(obj); - str = qobject_to_json_pretty(obj); - g_print("%s\n", qstring_get_str(str)); - QDECREF(str); - g_assert(g_strcmp0(qstring_get_str(qobject_to_qstring(obj)), "value2") == 0); - - /* QObject -> C type */ - mi = qmp_input_visitor_new(obj); - v = qmp_input_get_visitor(mi); - visit_type_EnumOne(v, &enum1_cpy, "unused", &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - g_debug("enum1_cpy, enum1: %d, %d", enum1_cpy, enum1); - g_assert(enum1_cpy == enum1); - - qobject_decref(obj); -} - -/* test enum values nested in schema-defined structs */ -static void test_nested_enums(void) -{ - QmpOutputVisitor *mo; - QmpInputVisitor *mi; - Visitor *v; - NestedEnumsOne *nested_enums, *nested_enums_cpy = NULL; - Error *err = NULL; - QObject *obj; - QString *str; - - nested_enums = g_malloc0(sizeof(NestedEnumsOne)); - nested_enums->enum1 = ENUM_ONE_VALUE1; - nested_enums->enum2 = ENUM_ONE_VALUE2; - nested_enums->enum3 = ENUM_ONE_VALUE3; - nested_enums->enum4 = ENUM_ONE_VALUE3; - nested_enums->has_enum2 = false; - nested_enums->has_enum4 = true; - - /* C type -> QObject */ - mo = qmp_output_visitor_new(); - v = qmp_output_get_visitor(mo); - visit_type_NestedEnumsOne(v, &nested_enums, NULL, &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - obj = qmp_output_get_qobject(mo); - g_assert(obj); - str = qobject_to_json_pretty(obj); - g_print("%s\n", qstring_get_str(str)); - QDECREF(str); - - /* QObject -> C type */ - mi = qmp_input_visitor_new(obj); - v = qmp_input_get_visitor(mi); - visit_type_NestedEnumsOne(v, &nested_enums_cpy, NULL, &err); - if (err) { - g_error("%s", error_get_pretty(err)); - } - g_assert(nested_enums_cpy); - g_assert(nested_enums_cpy->enum1 == nested_enums->enum1); - g_assert(nested_enums_cpy->enum3 == nested_enums->enum3); - g_assert(nested_enums_cpy->enum4 == nested_enums->enum4); - g_assert(nested_enums_cpy->has_enum2 == false); - g_assert(nested_enums_cpy->has_enum4 == true); - - qmp_output_visitor_cleanup(mo); - qmp_input_visitor_cleanup(mi); - qapi_free_NestedEnumsOne(nested_enums); - qapi_free_NestedEnumsOne(nested_enums_cpy); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/0.15/visitor_core", test_visitor_core); - g_test_add_func("/0.15/nested_structs", test_nested_structs); - g_test_add_func("/0.15/enums", test_enums); - g_test_add_func("/0.15/nested_enums", test_nested_enums); - - g_test_run(); - - return 0; -} diff --git a/ui/vnc-jobs-async.c b/ui/vnc-jobs-async.c index de5ea6b5d8..9b3016c129 100644 --- a/ui/vnc-jobs-async.c +++ b/ui/vnc-jobs-async.c @@ -318,7 +318,7 @@ void vnc_start_worker_thread(void) return ; q = vnc_queue_init(); - qemu_thread_create(&q->thread, vnc_worker_thread, q); + qemu_thread_create(&q->thread, vnc_worker_thread, q, QEMU_THREAD_DETACHED); queue = q; /* Set global queue */ } |