diff options
156 files changed, 6341 insertions, 2612 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 2d219d2ea0..6d864c1ceb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -405,6 +405,14 @@ M: Alexander Graf <agraf@suse.de> S: Maintained F: hw/s390-*.c +UniCore32 Machines +------------- +PKUnity-3 SoC initramfs-with-busybox +M: Guan Xuetao <gxt@mprc.pku.edu.cn> +S: Maintained +F: hw/puv3* +F: hw/unicore32/ + X86 Machines ------------ PC @@ -560,7 +568,7 @@ F: monitor.c Network device layer M: Anthony Liguori <aliguori@us.ibm.com> -M: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> +M: Stefan Hajnoczi <stefanha@gmail.com> S: Maintained F: net/ T: git git://github.com/stefanha/qemu.git net @@ -580,7 +588,7 @@ F: slirp/ T: git git://git.kiszka.org/qemu.git queues/slirp Tracing -M: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> +M: Stefan Hajnoczi <stefanha@gmail.com> S: Maintained F: trace/ F: scripts/tracetool.py diff --git a/Makefile b/Makefile index 000b46c379..d736ea5311 100644 --- a/Makefile +++ b/Makefile @@ -181,24 +181,26 @@ ifneq ($(wildcard config-host.mak),) include $(SRC_PATH)/tests/Makefile endif +qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py + qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\ -$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py +$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, " GEN $@") qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h :\ -$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py +$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, " GEN $@") qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\ -$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py +$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, " GEN $@") qapi-types.c qapi-types.h :\ -$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py +$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "." < $<, " GEN $@") qapi-visit.c qapi-visit.h :\ -$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py +$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "." < $<, " GEN $@") qmp-commands.h qmp-marshal.c :\ -$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py +$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, " GEN $@") QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h) diff --git a/Makefile.objs b/Makefile.objs index 5ebbcfa171..309d066286 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -77,6 +77,7 @@ common-obj-y += qemu-char.o #aio.o common-obj-y += block-migration.o iohandler.o common-obj-y += pflib.o common-obj-y += bitmap.o bitops.o +common-obj-y += page_cache.o common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o common-obj-$(CONFIG_WIN32) += version.o @@ -211,6 +212,7 @@ common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y) # qapi qapi-obj-y = qapi/ +qapi-obj-y += qapi-types.o qapi-visit.o common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o common-obj-y += qmp.o hmp.o diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt index 9ba7079589..287805825f 100644 --- a/QMP/qmp-events.txt +++ b/QMP/qmp-events.txt @@ -1,6 +1,23 @@ QEMU Monitor Protocol Events ============================ +BALLOON_CHANGE +-------------- + +Emitted when the guest changes the actual BALLOON level. This +value is equivalent to the 'actual' field return by the +'query-balloon' command + +Data: + +- "actual": actual level of the guest memory balloon in bytes (json-number) + +Example: + +{ "event": "BALLOON_CHANGE", + "data": { "actual": 944766976 }, + "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } + BLOCK_IO_ERROR -------------- @@ -26,6 +43,57 @@ Example: Note: If action is "stop", a STOP event will eventually follow the BLOCK_IO_ERROR event. +BLOCK_JOB_CANCELLED +------------------- + +Emitted when a block job has been cancelled. + +Data: + +- "type": Job type ("stream" for image streaming, json-string) +- "device": Device name (json-string) +- "len": Maximum progress value (json-int) +- "offset": Current progress value (json-int) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) + +Example: + +{ "event": "BLOCK_JOB_CANCELLED", + "data": { "type": "stream", "device": "virtio-disk0", + "len": 10737418240, "offset": 134217728, + "speed": 0 }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + +BLOCK_JOB_COMPLETED +------------------- + +Emitted when a block job has completed. + +Data: + +- "type": Job type ("stream" for image streaming, json-string) +- "device": Device name (json-string) +- "len": Maximum progress value (json-int) +- "offset": Current progress value (json-int) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) +- "error": Error message (json-string, optional) + Only present on failure. This field contains a human-readable + error message. There are no semantics other than that streaming + has failed and clients should not try to interpret the error + string. + +Example: + +{ "event": "BLOCK_JOB_COMPLETED", + "data": { "type": "stream", "device": "virtio-disk0", + "len": 10737418240, "offset": 10737418240, + "speed": 0 }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + DEVICE_TRAY_MOVED ----------------- @@ -98,6 +166,68 @@ Example: Note: If the command-line option "-no-shutdown" has been specified, a STOP event will eventually follow the SHUTDOWN event. +SPICE_CONNECTED, SPICE_DISCONNECTED +----------------------------------- + +Emitted when a SPICE client connects or disconnects. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, + "event": "SPICE_CONNECTED", + "data": { + "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} +}} + +SPICE_INITIALIZED +----------------- + +Emitted after initial handshake and authentication takes place (if any) +and the SPICE channel is up'n'running + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "connection-id": spice connection id. All channels with the same id + belong to the same spice session (json-int) + - "channel-type": channel type. "1" is the main control channel, filter for + this one if you want track spice sessions only (json-int) + - "channel-id": channel id. Usually "0", might be different needed when + multiple channels of the same type exist, such as multiple + display channels in a multihead setup (json-int) + - "tls": whevener the channel is encrypted (json-bool) + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, + "event": "SPICE_INITIALIZED", + "data": {"server": {"auth": "spice", "port": "5921", + "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "49004", "family": "ipv4", "channel-type": 3, + "connection-id": 1804289383, "host": "127.0.0.1", + "channel-id": 0, "tls": true} +}} + STOP ---- @@ -110,6 +240,32 @@ Example: { "event": "STOP", "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } +SUSPEND +------- + +Emitted when guest enters S3 state. + +Data: None. + +Example: + +{ "event": "SUSPEND", + "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } + +SUSPEND_DISK +------------ + +Emitted when the guest makes a request to enter S4 state. + +Data: None. + +Example: + +{ "event": "SUSPEND_DISK", + "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } + +Note: QEMU shuts down when entering S4 state. + VNC_CONNECTED ------------- @@ -200,69 +356,17 @@ Example: "host": "127.0.0.1", "sasl_username": "luiz" } }, "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } -SPICE_CONNECTED, SPICE_DISCONNECTED ------------------------------------ - -Emitted when a SPICE client connects or disconnects. - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") -- "client": Client information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - -Example: - -{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, - "event": "SPICE_CONNECTED", - "data": { - "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, - "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} -}} - - -SPICE_INITIALIZED ------------------ - -Emitted after initial handshake and authentication takes place (if any) -and the SPICE channel is up'n'running +WAKEUP +------ -Data: +Emitted when the guest has woken up from S3 and is running. -- "server": Server information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "auth": authentication method (json-string, optional) -- "client": Client information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "connection-id": spice connection id. All channels with the same id - belong to the same spice session (json-int) - - "channel-type": channel type. "1" is the main control channel, filter for - this one if you want track spice sessions only (json-int) - - "channel-id": channel id. Usually "0", might be different needed when - multiple channels of the same type exist, such as multiple - display channels in a multihead setup (json-int) - - "tls": whevener the channel is encrypted (json-bool) +Data: None. Example: -{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, - "event": "SPICE_INITIALIZED", - "data": {"server": {"auth": "spice", "port": "5921", - "family": "ipv4", "host": "127.0.0.1"}, - "client": {"port": "49004", "family": "ipv4", "channel-type": 3, - "connection-id": 1804289383, "host": "127.0.0.1", - "channel-id": 0, "tls": true} -}} - +{ "event": "WATCHDOG", + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } WATCHDOG -------- @@ -282,74 +386,3 @@ Example: Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is followed respectively by the RESET, SHUTDOWN, or STOP events. - - -BLOCK_JOB_COMPLETED -------------------- - -Emitted when a block job has completed. - -Data: - -- "type": Job type ("stream" for image streaming, json-string) -- "device": Device name (json-string) -- "len": Maximum progress value (json-int) -- "offset": Current progress value (json-int) - On success this is equal to len. - On failure this is less than len. -- "speed": Rate limit, bytes per second (json-int) -- "error": Error message (json-string, optional) - Only present on failure. This field contains a human-readable - error message. There are no semantics other than that streaming - has failed and clients should not try to interpret the error - string. - -Example: - -{ "event": "BLOCK_JOB_COMPLETED", - "data": { "type": "stream", "device": "virtio-disk0", - "len": 10737418240, "offset": 10737418240, - "speed": 0 }, - "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } - - -BLOCK_JOB_CANCELLED -------------------- - -Emitted when a block job has been cancelled. - -Data: - -- "type": Job type ("stream" for image streaming, json-string) -- "device": Device name (json-string) -- "len": Maximum progress value (json-int) -- "offset": Current progress value (json-int) - On success this is equal to len. - On failure this is less than len. -- "speed": Rate limit, bytes per second (json-int) - -Example: - -{ "event": "BLOCK_JOB_CANCELLED", - "data": { "type": "stream", "device": "virtio-disk0", - "len": 10737418240, "offset": 134217728, - "speed": 0 }, - "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } - - -BALLOON_CHANGE ----------- - -Emitted when the guest changes the actual BALLOON level. This -value is equivalent to the 'actual' field return by the -'query-balloon' command - -Data: - -- "actual": actual level of the guest memory balloon in bytes (json-number) - -Example: - -{ "event": "BALLOON_CHANGE", - "data": { "actual": 944766976 }, - "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } diff --git a/QMP/qmp-spec.txt b/QMP/qmp-spec.txt index 1ba916c9f2..a27789692b 100644 --- a/QMP/qmp-spec.txt +++ b/QMP/qmp-spec.txt @@ -106,14 +106,11 @@ completed because of an error condition. The format is: -{ "error": { "class": json-string, "data": json-object, "desc": json-string }, - "id": json-value } +{ "error": { "class": json-string, "desc": json-string }, "id": json-value } Where, -- The "class" member contains the error class name (eg. "ServiceUnavailable") -- The "data" member contains specific error data and is defined in a - per-command basis, it will be an empty json-object if the error has no data +- The "class" member contains the error class name (eg. "GenericError") - The "desc" member is a human-readable error message. Clients should not attempt to parse this message. - The "id" member contains the transaction identification associated with @@ -173,8 +170,7 @@ S: {"return": {"enabled": true, "present": true}, "id": "example"} ------------------ C: { "execute": } -S: {"error": {"class": "JSONParsing", "desc": "Invalid JSON syntax", "data": -{}}} +S: {"error": {"class": "GenericError", "desc": "Invalid JSON syntax" } } 3.5 Powerdown event ------------------- diff --git a/arch_init.c b/arch_init.c index 60823baabd..9b46bfcaa5 100644 --- a/arch_init.c +++ b/arch_init.c @@ -43,6 +43,7 @@ #include "hw/smbios.h" #include "exec-memory.h" #include "hw/pcspk.h" +#include "qemu/page_cache.h" #ifdef DEBUG_ARCH_INIT #define DPRINTF(fmt, ...) \ @@ -91,6 +92,8 @@ int graphic_depth = 15; #define QEMU_ARCH QEMU_ARCH_SPARC #elif defined(TARGET_XTENSA) #define QEMU_ARCH QEMU_ARCH_XTENSA +#elif defined(TARGET_UNICORE32) +#define QEMU_ARCH QEMU_ARCH_UNICORE32 #endif const uint32_t arch_type = QEMU_ARCH; @@ -104,6 +107,7 @@ const uint32_t arch_type = QEMU_ARCH; #define RAM_SAVE_FLAG_PAGE 0x08 #define RAM_SAVE_FLAG_EOS 0x10 #define RAM_SAVE_FLAG_CONTINUE 0x20 +#define RAM_SAVE_FLAG_XBZRLE 0x40 #ifdef __ALTIVEC__ #include <altivec.h> @@ -171,6 +175,92 @@ static int is_dup_page(uint8_t *page) return 1; } +/* struct contains XBZRLE cache and a static page + used by the compression */ +static struct { + /* buffer used for XBZRLE encoding */ + uint8_t *encoded_buf; + /* buffer for storing page content */ + uint8_t *current_buf; + /* buffer used for XBZRLE decoding */ + uint8_t *decoded_buf; + /* Cache for XBZRLE */ + PageCache *cache; +} XBZRLE = { + .encoded_buf = NULL, + .current_buf = NULL, + .decoded_buf = NULL, + .cache = NULL, +}; + + +int64_t xbzrle_cache_resize(int64_t new_size) +{ + if (XBZRLE.cache != NULL) { + return cache_resize(XBZRLE.cache, new_size / TARGET_PAGE_SIZE) * + TARGET_PAGE_SIZE; + } + return pow2floor(new_size); +} + +/* accounting for migration statistics */ +typedef struct AccountingInfo { + uint64_t dup_pages; + uint64_t norm_pages; + uint64_t iterations; + uint64_t xbzrle_bytes; + uint64_t xbzrle_pages; + uint64_t xbzrle_cache_miss; + uint64_t xbzrle_overflows; +} AccountingInfo; + +static AccountingInfo acct_info; + +static void acct_clear(void) +{ + memset(&acct_info, 0, sizeof(acct_info)); +} + +uint64_t dup_mig_bytes_transferred(void) +{ + return acct_info.dup_pages * TARGET_PAGE_SIZE; +} + +uint64_t dup_mig_pages_transferred(void) +{ + return acct_info.dup_pages; +} + +uint64_t norm_mig_bytes_transferred(void) +{ + return acct_info.norm_pages * TARGET_PAGE_SIZE; +} + +uint64_t norm_mig_pages_transferred(void) +{ + return acct_info.norm_pages; +} + +uint64_t xbzrle_mig_bytes_transferred(void) +{ + return acct_info.xbzrle_bytes; +} + +uint64_t xbzrle_mig_pages_transferred(void) +{ + return acct_info.xbzrle_pages; +} + +uint64_t xbzrle_mig_pages_cache_miss(void) +{ + return acct_info.xbzrle_cache_miss; +} + +uint64_t xbzrle_mig_pages_overflow(void) +{ + return acct_info.xbzrle_overflows; +} + static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset, int cont, int flag) { @@ -183,6 +273,61 @@ static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset, } +#define ENCODING_FLAG_XBZRLE 0x1 + +static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data, + ram_addr_t current_addr, RAMBlock *block, + ram_addr_t offset, int cont, bool last_stage) +{ + int encoded_len = 0, bytes_sent = -1; + uint8_t *prev_cached_page; + + if (!cache_is_cached(XBZRLE.cache, current_addr)) { + if (!last_stage) { + cache_insert(XBZRLE.cache, current_addr, + g_memdup(current_data, TARGET_PAGE_SIZE)); + } + acct_info.xbzrle_cache_miss++; + return -1; + } + + prev_cached_page = get_cached_data(XBZRLE.cache, current_addr); + + /* save current buffer into memory */ + memcpy(XBZRLE.current_buf, current_data, TARGET_PAGE_SIZE); + + /* XBZRLE encoding (if there is no overflow) */ + encoded_len = xbzrle_encode_buffer(prev_cached_page, XBZRLE.current_buf, + TARGET_PAGE_SIZE, XBZRLE.encoded_buf, + TARGET_PAGE_SIZE); + if (encoded_len == 0) { + DPRINTF("Skipping unmodified page\n"); + return 0; + } else if (encoded_len == -1) { + DPRINTF("Overflow\n"); + acct_info.xbzrle_overflows++; + /* update data in the cache */ + memcpy(prev_cached_page, current_data, TARGET_PAGE_SIZE); + return -1; + } + + /* we need to update the data in the cache, in order to get the same data */ + if (!last_stage) { + memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE); + } + + /* Send XBZRLE based compressed page */ + save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_XBZRLE); + qemu_put_byte(f, ENCODING_FLAG_XBZRLE); + qemu_put_be16(f, encoded_len); + qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len); + bytes_sent = encoded_len + 1 + 2; + acct_info.xbzrle_pages++; + acct_info.xbzrle_bytes += bytes_sent; + + return bytes_sent; +} + static RAMBlock *last_block; static ram_addr_t last_offset; @@ -194,12 +339,13 @@ static ram_addr_t last_offset; * n: the amount of bytes written in other case */ -static int ram_save_block(QEMUFile *f) +static int ram_save_block(QEMUFile *f, bool last_stage) { RAMBlock *block = last_block; ram_addr_t offset = last_offset; int bytes_sent = -1; MemoryRegion *mr; + ram_addr_t current_addr; if (!block) block = QLIST_FIRST(&ram_list.blocks); @@ -217,16 +363,31 @@ static int ram_save_block(QEMUFile *f) p = memory_region_get_ram_ptr(mr) + offset; if (is_dup_page(p)) { + acct_info.dup_pages++; save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS); qemu_put_byte(f, *p); bytes_sent = 1; - } else { + } else if (migrate_use_xbzrle()) { + current_addr = block->offset + offset; + bytes_sent = save_xbzrle_page(f, p, current_addr, block, + offset, cont, last_stage); + if (!last_stage) { + p = get_cached_data(XBZRLE.cache, current_addr); + } + } + + /* either we didn't send yet (we may have had XBZRLE overflow) */ + if (bytes_sent == -1) { save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE); qemu_put_buffer(f, p, TARGET_PAGE_SIZE); bytes_sent = TARGET_PAGE_SIZE; + acct_info.norm_pages++; } - break; + /* if page is unmodified, continue to the next */ + if (bytes_sent != 0) { + break; + } } offset += TARGET_PAGE_SIZE; @@ -304,6 +465,15 @@ static void sort_ram_list(void) static void migration_end(void) { memory_global_dirty_log_stop(); + + if (migrate_use_xbzrle()) { + cache_fini(XBZRLE.cache); + g_free(XBZRLE.cache); + g_free(XBZRLE.encoded_buf); + g_free(XBZRLE.current_buf); + g_free(XBZRLE.decoded_buf); + XBZRLE.cache = NULL; + } } static void ram_migration_cancel(void *opaque) @@ -323,6 +493,19 @@ static int ram_save_setup(QEMUFile *f, void *opaque) last_offset = 0; sort_ram_list(); + if (migrate_use_xbzrle()) { + XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() / + TARGET_PAGE_SIZE, + TARGET_PAGE_SIZE); + if (!XBZRLE.cache) { + DPRINTF("Error creating cache\n"); + return -1; + } + XBZRLE.encoded_buf = g_malloc0(TARGET_PAGE_SIZE); + XBZRLE.current_buf = g_malloc(TARGET_PAGE_SIZE); + acct_clear(); + } + /* Make sure all dirty bits are set */ QLIST_FOREACH(block, &ram_list.blocks, next) { for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) { @@ -363,12 +546,13 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) while ((ret = qemu_file_rate_limit(f)) == 0) { int bytes_sent; - bytes_sent = ram_save_block(f); + bytes_sent = ram_save_block(f, false); /* no more blocks to sent */ if (bytes_sent < 0) { break; } bytes_transferred += bytes_sent; + acct_info.iterations++; /* we want to check in the 1st loop, just in case it was the 1st time and we had to sync the dirty bitmap. qemu_get_clock_ns() is a bit expensive, so we only check each some @@ -424,7 +608,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) while (true) { int bytes_sent; - bytes_sent = ram_save_block(f); + bytes_sent = ram_save_block(f, true); /* no more blocks to sent */ if (bytes_sent < 0) { break; @@ -438,6 +622,47 @@ static int ram_save_complete(QEMUFile *f, void *opaque) return 0; } +static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) +{ + int ret, rc = 0; + unsigned int xh_len; + int xh_flags; + + if (!XBZRLE.decoded_buf) { + XBZRLE.decoded_buf = g_malloc(TARGET_PAGE_SIZE); + } + + /* extract RLE header */ + xh_flags = qemu_get_byte(f); + xh_len = qemu_get_be16(f); + + if (xh_flags != ENCODING_FLAG_XBZRLE) { + fprintf(stderr, "Failed to load XBZRLE page - wrong compression!\n"); + return -1; + } + + if (xh_len > TARGET_PAGE_SIZE) { + fprintf(stderr, "Failed to load XBZRLE page - len overflow!\n"); + return -1; + } + /* load data and decode */ + qemu_get_buffer(f, XBZRLE.decoded_buf, xh_len); + + /* decode RLE */ + ret = xbzrle_decode_buffer(XBZRLE.decoded_buf, xh_len, host, + TARGET_PAGE_SIZE); + if (ret == -1) { + fprintf(stderr, "Failed to load XBZRLE page - decode error!\n"); + rc = -1; + } else if (ret > TARGET_PAGE_SIZE) { + fprintf(stderr, "Failed to load XBZRLE page - size %d exceeds %d!\n", + ret, TARGET_PAGE_SIZE); + abort(); + } + + return rc; +} + static inline void *host_from_stream_offset(QEMUFile *f, ram_addr_t offset, int flags) @@ -551,6 +776,19 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) } qemu_get_buffer(f, host, TARGET_PAGE_SIZE); + } else if (flags & RAM_SAVE_FLAG_XBZRLE) { + if (!migrate_use_xbzrle()) { + return -EINVAL; + } + void *host = host_from_stream_offset(f, addr, flags); + if (!host) { + return -EINVAL; + } + + if (load_xbzrle(f, addr, host) < 0) { + ret = -EINVAL; + goto done; + } } error = qemu_file_get_error(f); if (error) { diff --git a/arch_init.h b/arch_init.h index 3dfea3b4f3..547f93cd1d 100644 --- a/arch_init.h +++ b/arch_init.h @@ -17,6 +17,7 @@ enum { QEMU_ARCH_SPARC = 2048, QEMU_ARCH_XTENSA = 4096, QEMU_ARCH_OPENRISC = 8192, + QEMU_ARCH_UNICORE32 = 0x4000, }; extern const uint32_t arch_type; diff --git a/block.c b/block.c index 24323c11d0..016858bf8c 100644 --- a/block.c +++ b/block.c @@ -2445,6 +2445,7 @@ BlockInfoList *qmp_query_block(Error **errp) info->value->inserted->ro = bs->read_only; info->value->inserted->drv = g_strdup(bs->drv->format_name); info->value->inserted->encrypted = bs->encrypted; + info->value->inserted->encryption_key_missing = bdrv_key_required(bs); if (bs->backing_file[0]) { info->value->inserted->has_backing_file = true; info->value->inserted->backing_file = g_strdup(bs->backing_file); diff --git a/block.h b/block.h index 650d872f46..2e2be11071 100644 --- a/block.h +++ b/block.h @@ -79,6 +79,7 @@ typedef struct BlockDevOps { #define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */ #define BDRV_O_COPY_ON_READ 0x0400 /* copy read backing sectors into image */ #define BDRV_O_INCOMING 0x0800 /* consistency hint for incoming migration */ +#define BDRV_O_CHECK 0x1000 /* open solely for consistency check */ #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH) diff --git a/block/iscsi.c b/block/iscsi.c index 993a86d829..219f927823 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -896,26 +896,26 @@ static char *parse_initiator_name(const char *target) QemuOptsList *list; QemuOpts *opts; const char *name = NULL; + const char *iscsi_name = qemu_get_vm_name(); list = qemu_find_opts("iscsi"); - if (!list) { - return g_strdup("iqn.2008-11.org.linux-kvm"); - } - - opts = qemu_opts_find(list, target); - if (opts == NULL) { - opts = QTAILQ_FIRST(&list->head); + if (list) { + opts = qemu_opts_find(list, target); if (!opts) { - return g_strdup("iqn.2008-11.org.linux-kvm"); + opts = QTAILQ_FIRST(&list->head); + } + if (opts) { + name = qemu_opt_get(opts, "initiator-name"); } } - name = qemu_opt_get(opts, "initiator-name"); - if (!name) { - return g_strdup("iqn.2008-11.org.linux-kvm"); + if (name) { + return g_strdup(name); + } else { + return g_strdup_printf("iqn.2008-11.org.linux-kvm%s%s", + iscsi_name ? ":" : "", + iscsi_name ? iscsi_name : ""); } - - return g_strdup(name); } /* @@ -943,7 +943,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) error_report("Failed to parse URL : %s %s", filename, iscsi_get_error(iscsi)); ret = -EINVAL; - goto failed; + goto out; } memset(iscsilun, 0, sizeof(IscsiLun)); @@ -954,13 +954,13 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) if (iscsi == NULL) { error_report("iSCSI: Failed to create iSCSI context."); ret = -ENOMEM; - goto failed; + goto out; } if (iscsi_set_targetname(iscsi, iscsi_url->target)) { error_report("iSCSI: Failed to set target name."); ret = -EINVAL; - goto failed; + goto out; } if (iscsi_url->user != NULL) { @@ -969,7 +969,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) if (ret != 0) { error_report("Failed to set initiator username and password"); ret = -EINVAL; - goto failed; + goto out; } } @@ -977,13 +977,13 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) if (parse_chap(iscsi, iscsi_url->target) != 0) { error_report("iSCSI: Failed to set CHAP user/password"); ret = -EINVAL; - goto failed; + goto out; } if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) { error_report("iSCSI: Failed to set session type to normal."); ret = -EINVAL; - goto failed; + goto out; } iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); @@ -1004,7 +1004,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) != 0) { error_report("iSCSI: Failed to start async connect."); ret = -EINVAL; - goto failed; + goto out; } while (!task.complete) { @@ -1015,11 +1015,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) error_report("iSCSI: Failed to connect to LUN : %s", iscsi_get_error(iscsi)); ret = -EINVAL; - goto failed; - } - - if (iscsi_url != NULL) { - iscsi_destroy_url(iscsi_url); + goto out; } /* Medium changer or tape. We dont have any emulation for this so this must @@ -1031,19 +1027,22 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) bs->sg = 1; } - return 0; + ret = 0; -failed: +out: if (initiator_name != NULL) { g_free(initiator_name); } if (iscsi_url != NULL) { iscsi_destroy_url(iscsi_url); } - if (iscsi != NULL) { - iscsi_destroy_context(iscsi); + + if (ret) { + if (iscsi != NULL) { + iscsi_destroy_context(iscsi); + } + memset(iscsilun, 0, sizeof(IscsiLun)); } - memset(iscsilun, 0, sizeof(IscsiLun)); return ret; } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index d7e0e19d9c..e179211c57 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -662,7 +662,10 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) qcow2_cache_depends_on_flush(s->l2_table_cache); } - qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); + if (qcow2_need_accurate_refcounts(s)) { + qcow2_cache_set_dependency(bs, s->l2_table_cache, + s->refcount_block_cache); + } ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index); if (ret < 0) { goto err; diff --git a/block/qcow2.c b/block/qcow2.c index 870148ddf8..8f183f1465 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -214,6 +214,76 @@ static void report_unsupported_feature(BlockDriverState *bs, } } +/* + * Sets the dirty bit and flushes afterwards if necessary. + * + * The incompatible_features bit is only set if the image file header was + * updated successfully. Therefore it is not required to check the return + * value of this function. + */ +static int qcow2_mark_dirty(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + uint64_t val; + int ret; + + assert(s->qcow_version >= 3); + + if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { + return 0; /* already dirty */ + } + + val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY); + ret = bdrv_pwrite(bs->file, offsetof(QCowHeader, incompatible_features), + &val, sizeof(val)); + if (ret < 0) { + return ret; + } + ret = bdrv_flush(bs->file); + if (ret < 0) { + return ret; + } + + /* Only treat image as dirty if the header was updated successfully */ + s->incompatible_features |= QCOW2_INCOMPAT_DIRTY; + return 0; +} + +/* + * Clears the dirty bit and flushes before if necessary. Only call this + * function when there are no pending requests, it does not guard against + * concurrent requests dirtying the image. + */ +static int qcow2_mark_clean(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + + if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { + int ret = bdrv_flush(bs); + if (ret < 0) { + return ret; + } + + s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY; + return qcow2_update_header(bs); + } + return 0; +} + +static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result, + BdrvCheckMode fix) +{ + int ret = qcow2_check_refcounts(bs, result, fix); + if (ret < 0) { + return ret; + } + + if (fix && result->check_errors == 0 && result->corruptions == 0) { + return qcow2_mark_clean(bs); + } + return ret; +} + static int qcow2_open(BlockDriverState *bs, int flags) { BDRVQcowState *s = bs->opaque; @@ -287,12 +357,13 @@ static int qcow2_open(BlockDriverState *bs, int flags) s->compatible_features = header.compatible_features; s->autoclear_features = header.autoclear_features; - if (s->incompatible_features != 0) { + if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) { void *feature_table = NULL; qcow2_read_extensions(bs, header.header_length, ext_end, &feature_table); report_unsupported_feature(bs, feature_table, - s->incompatible_features); + s->incompatible_features & + ~QCOW2_INCOMPAT_MASK); ret = -ENOTSUP; goto fail; } @@ -412,6 +483,17 @@ static int qcow2_open(BlockDriverState *bs, int flags) /* Initialise locks */ qemu_co_mutex_init(&s->lock); + /* Repair image if dirty */ + if (!(flags & BDRV_O_CHECK) && !bs->read_only && + (s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) { + BdrvCheckResult result = {0}; + + ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS); + if (ret < 0) { + goto fail; + } + } + #ifdef DEBUG_ALLOC { BdrvCheckResult result = {0}; @@ -714,6 +796,11 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, goto fail; } + if (l2meta.nb_clusters > 0 && + (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)) { + qcow2_mark_dirty(bs); + } + cluster_offset = l2meta.cluster_offset; assert((cluster_offset & 511) == 0); @@ -785,6 +872,8 @@ static void qcow2_close(BlockDriverState *bs) qcow2_cache_flush(bs, s->l2_table_cache); qcow2_cache_flush(bs, s->refcount_block_cache); + qcow2_mark_clean(bs); + qcow2_cache_destroy(bs, s->l2_table_cache); qcow2_cache_destroy(bs, s->refcount_block_cache); @@ -949,7 +1038,16 @@ int qcow2_update_header(BlockDriverState *bs) /* Feature table */ Qcow2Feature features[] = { - /* no feature defined yet */ + { + .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, + .bit = QCOW2_INCOMPAT_DIRTY_BITNR, + .name = "dirty bit", + }, + { + .type = QCOW2_FEAT_TYPE_COMPATIBLE, + .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, + .name = "lazy refcounts", + }, }; ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE, @@ -1132,6 +1230,11 @@ static int qcow2_create2(const char *filename, int64_t total_size, header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); } + if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) { + header.compatible_features |= + cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS); + } + ret = bdrv_pwrite(bs, 0, &header, sizeof(header)); if (ret < 0) { goto out; @@ -1245,6 +1348,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) options->value.s); return -EINVAL; } + } else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) { + flags |= options->value.n ? BLOCK_FLAG_LAZY_REFCOUNTS : 0; } options++; } @@ -1255,6 +1360,12 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options) return -EINVAL; } + if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) { + fprintf(stderr, "Lazy refcounts only supported with compatibility " + "level 1.1 and above (use compat=1.1 or greater)\n"); + return -EINVAL; + } + return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags, cluster_size, prealloc, options, version); } @@ -1441,10 +1552,12 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) return ret; } - ret = qcow2_cache_flush(bs, s->refcount_block_cache); - if (ret < 0) { - qemu_co_mutex_unlock(&s->lock); - return ret; + if (qcow2_need_accurate_refcounts(s)) { + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + qemu_co_mutex_unlock(&s->lock); + return ret; + } } qemu_co_mutex_unlock(&s->lock); @@ -1464,13 +1577,6 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } - -static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result, - BdrvCheckMode fix) -{ - return qcow2_check_refcounts(bs, result, fix); -} - #if 0 static void dump_refcounts(BlockDriverState *bs) { @@ -1559,6 +1665,11 @@ static QEMUOptionParameter qcow2_create_options[] = { .type = OPT_STRING, .help = "Preallocation mode (allowed values: off, metadata)" }, + { + .name = BLOCK_OPT_LAZY_REFCOUNTS, + .type = OPT_FLAG, + .help = "Postpone refcount updates", + }, { NULL } }; diff --git a/block/qcow2.h b/block/qcow2.h index 455b6d7cfe..b4eb65470e 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -110,6 +110,22 @@ enum { QCOW2_FEAT_TYPE_AUTOCLEAR = 2, }; +/* Incompatible feature bits */ +enum { + QCOW2_INCOMPAT_DIRTY_BITNR = 0, + QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR, + + QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY, +}; + +/* Compatible feature bits */ +enum { + QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR = 0, + QCOW2_COMPAT_LAZY_REFCOUNTS = 1 << QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, + + QCOW2_COMPAT_FEAT_MASK = QCOW2_COMPAT_LAZY_REFCOUNTS, +}; + typedef struct Qcow2Feature { uint8_t type; uint8_t bit; @@ -237,6 +253,11 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry) } } +/* Check whether refcounts are eager or lazy */ +static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s) +{ + return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY); +} // FIXME Need qcow2_ prefix to global functions diff --git a/block/qed-check.c b/block/qed-check.c index 5edf60775b..b473dcd61f 100644 --- a/block/qed-check.c +++ b/block/qed-check.c @@ -194,6 +194,28 @@ static void qed_check_for_leaks(QEDCheck *check) } } +/** + * Mark an image clean once it passes check or has been repaired + */ +static void qed_check_mark_clean(BDRVQEDState *s, BdrvCheckResult *result) +{ + /* Skip if there were unfixable corruptions or I/O errors */ + if (result->corruptions > 0 || result->check_errors > 0) { + return; + } + + /* Skip if image is already marked clean */ + if (!(s->header.features & QED_F_NEED_CHECK)) { + return; + } + + /* Ensure fixes reach storage before clearing check bit */ + bdrv_flush(s->bs); + + s->header.features &= ~QED_F_NEED_CHECK; + qed_write_header_sync(s); +} + int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix) { QEDCheck check = { @@ -215,6 +237,10 @@ int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix) if (ret == 0) { /* Only check for leaks if entire image was scanned successfully */ qed_check_for_leaks(&check); + + if (fix) { + qed_check_mark_clean(s, result); + } } g_free(check.used_clusters); diff --git a/block/qed.c b/block/qed.c index 5f3eefa3af..a02dbfd72d 100644 --- a/block/qed.c +++ b/block/qed.c @@ -89,7 +89,7 @@ static void qed_header_cpu_to_le(const QEDHeader *cpu, QEDHeader *le) le->backing_filename_size = cpu_to_le32(cpu->backing_filename_size); } -static int qed_write_header_sync(BDRVQEDState *s) +int qed_write_header_sync(BDRVQEDState *s) { QEDHeader le; int ret; @@ -477,7 +477,7 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags) } /* If image was not closed cleanly, check consistency */ - if (s->header.features & QED_F_NEED_CHECK) { + if (!(flags & BDRV_O_CHECK) && (s->header.features & QED_F_NEED_CHECK)) { /* Read-only images cannot be fixed. There is no risk of corruption * since write operations are not possible. Therefore, allow * potentially inconsistent images to be opened read-only. This can @@ -491,13 +491,6 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags) if (ret) { goto out; } - if (!result.corruptions && !result.check_errors) { - /* Ensure fixes reach storage before clearing check bit */ - bdrv_flush(s->bs); - - s->header.features &= ~QED_F_NEED_CHECK; - qed_write_header_sync(s); - } } } diff --git a/block/qed.h b/block/qed.h index c716772ad7..a063bf70af 100644 --- a/block/qed.h +++ b/block/qed.h @@ -211,6 +211,11 @@ void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque); void gencb_complete(void *opaque, int ret); /** + * Header functions + */ +int qed_write_header_sync(BDRVQEDState *s); + +/** * L2 cache functions */ void qed_init_l2_cache(L2TableCache *l2_cache); diff --git a/block_int.h b/block_int.h index d72317fbe3..4452f6f398 100644 --- a/block_int.h +++ b/block_int.h @@ -30,9 +30,11 @@ #include "qemu-coroutine.h" #include "qemu-timer.h" #include "qapi-types.h" +#include "qerror.h" -#define BLOCK_FLAG_ENCRYPT 1 -#define BLOCK_FLAG_COMPAT6 4 +#define BLOCK_FLAG_ENCRYPT 1 +#define BLOCK_FLAG_COMPAT6 4 +#define BLOCK_FLAG_LAZY_REFCOUNTS 8 #define BLOCK_IO_LIMIT_READ 0 #define BLOCK_IO_LIMIT_WRITE 1 @@ -41,16 +43,17 @@ #define BLOCK_IO_SLICE_TIME 100000000 #define NANOSECONDS_PER_SECOND 1000000000.0 -#define BLOCK_OPT_SIZE "size" -#define BLOCK_OPT_ENCRYPT "encryption" -#define BLOCK_OPT_COMPAT6 "compat6" -#define BLOCK_OPT_BACKING_FILE "backing_file" -#define BLOCK_OPT_BACKING_FMT "backing_fmt" -#define BLOCK_OPT_CLUSTER_SIZE "cluster_size" -#define BLOCK_OPT_TABLE_SIZE "table_size" -#define BLOCK_OPT_PREALLOC "preallocation" -#define BLOCK_OPT_SUBFMT "subformat" -#define BLOCK_OPT_COMPAT_LEVEL "compat" +#define BLOCK_OPT_SIZE "size" +#define BLOCK_OPT_ENCRYPT "encryption" +#define BLOCK_OPT_COMPAT6 "compat6" +#define BLOCK_OPT_BACKING_FILE "backing_file" +#define BLOCK_OPT_BACKING_FMT "backing_fmt" +#define BLOCK_OPT_CLUSTER_SIZE "cluster_size" +#define BLOCK_OPT_TABLE_SIZE "table_size" +#define BLOCK_OPT_PREALLOC "preallocation" +#define BLOCK_OPT_SUBFMT "subformat" +#define BLOCK_OPT_COMPAT_LEVEL "compat" +#define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts" typedef struct BdrvTrackedRequest BdrvTrackedRequest; diff --git a/blockdev.c b/blockdev.c index 8669142704..7c83baa353 100644 --- a/blockdev.c +++ b/blockdev.c @@ -377,6 +377,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) } } + bdrv_flags |= BDRV_O_CACHE_WB; if ((buf = qemu_opt_get(opts, "cache")) != NULL) { if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) { error_report("invalid cache option"); diff --git a/compiler.h b/compiler.h index 736e77075a..f76921e5b0 100644 --- a/compiler.h +++ b/compiler.h @@ -45,6 +45,7 @@ # define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2))) # define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) # endif +#define GCC_WEAK __attribute__((weak)) #else #define GCC_ATTR /**/ #define GCC_FMT_ATTR(n, m) diff --git a/configure b/configure index 280726c3f8..fea62f1d29 100755 --- a/configure +++ b/configure @@ -27,16 +27,40 @@ printf " '%s'" "$0" "$@" >> config.log echo >> config.log echo "#" >> config.log +do_cc() { + # Run the compiler, capturing its output to the log. + echo $cc "$@" >> config.log + $cc "$@" >> config.log 2>&1 || return $? + # Test passed. If this is an --enable-werror build, rerun + # the test with -Werror and bail out if it fails. This + # makes warning-generating-errors in configure test code + # obvious to developers. + if test "$werror" != "yes"; then + return 0 + fi + # Don't bother rerunning the compile if we were already using -Werror + case "$*" in + *-Werror*) + return 0 + ;; + esac + echo $cc -Werror "$@" >> config.log + $cc -Werror "$@" >> config.log 2>&1 && return $? + echo "ERROR: configure test passed without -Werror but failed with -Werror." + echo "This is probably a bug in the configure script. The failing command" + echo "will be at the bottom of config.log." + echo "You can run configure with --disable-werror to bypass this check." + exit 1 +} + compile_object() { - echo $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log - $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log 2>&1 + do_cc $QEMU_CFLAGS -c -o $TMPO $TMPC } compile_prog() { local_cflags="$1" local_ldflags="$2" - echo $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log - $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log 2>&1 + do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags } # symbolically link $1 to $2. Portable version of "ln -sf". @@ -147,7 +171,6 @@ vhost_net="no" kvm="no" gprof="no" debug_tcg="no" -debug_mon="no" debug="no" strip_opt="yes" tcg_interpreter="no" @@ -633,14 +656,9 @@ for opt do ;; --disable-debug-tcg) debug_tcg="no" ;; - --enable-debug-mon) debug_mon="yes" - ;; - --disable-debug-mon) debug_mon="no" - ;; --enable-debug) # Enable debugging options that aren't excessively noisy debug_tcg="yes" - debug_mon="yes" debug="yes" strip_opt="no" ;; @@ -935,6 +953,7 @@ sparc64-softmmu \ s390x-softmmu \ xtensa-softmmu \ xtensaeb-softmmu \ +unicore32-softmmu \ " fi # the following are Linux specific @@ -2231,7 +2250,7 @@ cat > $TMPC <<EOF #include <sys/types.h> #include <sys/uio.h> #include <unistd.h> -int main(void) { return preadv == preadv; } +int main(void) { return preadv(0, 0, 0, 0); } EOF preadv=no if compile_prog "" "" ; then @@ -2527,7 +2546,7 @@ int main(void) * warning but not an error, and will proceed to fail the * qemu compile where we compile with -Werror.) */ - return epoll_create1 == epoll_create1; + return (int)(uintptr_t)&epoll_create1; } EOF if compile_prog "" "" ; then @@ -2920,7 +2939,7 @@ has_environ=no cat > $TMPC << EOF #include <unistd.h> int main(void) { - environ = environ; + environ = 0; return 0; } EOF @@ -3039,7 +3058,6 @@ echo "host CPU $cpu" echo "host big endian $bigendian" echo "target list $target_list" echo "tcg debug enabled $debug_tcg" -echo "Mon debug enabled $debug_mon" echo "gprof enabled $gprof" echo "sparse enabled $sparse" echo "strip binaries $strip_opt" @@ -3132,9 +3150,6 @@ echo "ARCH=$ARCH" >> $config_host_mak if test "$debug_tcg" = "yes" ; then echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak fi -if test "$debug_mon" = "yes" ; then - echo "CONFIG_DEBUG_MONITOR=y" >> $config_host_mak -fi if test "$debug" = "yes" ; then echo "CONFIG_DEBUG_EXEC=y" >> $config_host_mak fi @@ -3538,15 +3553,23 @@ if test "$linux" = "yes" ; then mkdir -p linux-headers case "$cpu" in i386|x86_64) - symlink "$source_path/linux-headers/asm-x86" linux-headers/asm + linux_arch=x86 ;; ppcemb|ppc|ppc64) - symlink "$source_path/linux-headers/asm-powerpc" linux-headers/asm + linux_arch=powerpc ;; s390x) - symlink "$source_path/linux-headers/asm-s390" linux-headers/asm + linux_arch=s390 + ;; + *) + # For most CPUs the kernel architecture name and QEMU CPU name match. + linux_arch="$cpu" ;; esac + # For non-KVM architectures we will not have asm headers + if [ -e "$source_path/linux-headers/asm-$linux_arch" ]; then + symlink "$source_path/linux-headers/asm-$linux_arch" linux-headers/asm + fi fi for target in $target_list; do diff --git a/cpu-exec.c b/cpu-exec.c index 543460c34c..134b3c4fcf 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -156,12 +156,9 @@ static inline TranslationBlock *tb_find_fast(CPUArchState *env) static CPUDebugExcpHandler *debug_excp_handler; -CPUDebugExcpHandler *cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler) +void cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler) { - CPUDebugExcpHandler *old_handler = debug_excp_handler; - debug_excp_handler = handler; - return old_handler; } static void cpu_handle_debug_exception(CPUArchState *env) @@ -447,6 +444,7 @@ int cpu_exec(CPUArchState *env) #elif defined(TARGET_UNICORE32) if (interrupt_request & CPU_INTERRUPT_HARD && !(env->uncached_asr & ASR_I)) { + env->exception_index = UC32_EXCP_INTR; do_interrupt(env); next_tb = 0; } diff --git a/cpus.c b/cpus.c index 3de2e27f41..e476a3cd5e 100644 --- a/cpus.c +++ b/cpus.c @@ -70,7 +70,8 @@ static bool cpu_thread_is_idle(CPUArchState *env) if (env->stopped || !runstate_is_running()) { return true; } - if (!env->halted || qemu_cpu_has_work(env) || kvm_irqchip_in_kernel()) { + if (!env->halted || qemu_cpu_has_work(env) || + kvm_async_interrupts_enabled()) { return false; } return true; diff --git a/cutils.c b/cutils.c index 9d4c570939..ee4614d378 100644 --- a/cutils.c +++ b/cutils.c @@ -382,3 +382,45 @@ int qemu_parse_fd(const char *param) } return fd; } + +/* round down to the nearest power of 2*/ +int64_t pow2floor(int64_t value) +{ + if (!is_power_of_2(value)) { + value = 0x8000000000000000ULL >> clz64(value); + } + return value; +} + +/* + * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) + * Input is limited to 14-bit numbers + */ +int uleb128_encode_small(uint8_t *out, uint32_t n) +{ + g_assert(n <= 0x3fff); + if (n < 0x80) { + *out++ = n; + return 1; + } else { + *out++ = (n & 0x7f) | 0x80; + *out++ = n >> 7; + return 2; + } +} + +int uleb128_decode_small(const uint8_t *in, uint32_t *n) +{ + if (!(*in & 0x80)) { + *n = *in++; + return 1; + } else { + *n = *in++ & 0x7f; + /* we exceed 14 bit number */ + if (*in & 0x80) { + return -1; + } + *n |= *in++ << 7; + return 2; + } +} diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index e542b4f8ee..f335a725f0 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -27,3 +27,21 @@ CONFIG_SMC91C111=y CONFIG_DS1338=y CONFIG_PFLASH_CFI01=y CONFIG_PFLASH_CFI02=y + +CONFIG_ARM_TIMER=y +CONFIG_PL011=y +CONFIG_PL022=y +CONFIG_PL031=y +CONFIG_PL041=y +CONFIG_PL050=y +CONFIG_PL061=y +CONFIG_PL080=y +CONFIG_PL110=y +CONFIG_PL181=y +CONFIG_PL190=y +CONFIG_PL310=y +CONFIG_CADENCE=y +CONFIG_XGMAC=y + +CONFIG_VERSATILE_PCI=y +CONFIG_VERSATILE_I2C=y diff --git a/default-configs/pci.mak b/default-configs/pci.mak index 9febb47ae6..69e18f1428 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -18,3 +18,4 @@ CONFIG_IDE_QDEV=y CONFIG_IDE_PCI=y CONFIG_AHCI=y CONFIG_ESP=y +CONFIG_ESP_PCI=y diff --git a/default-configs/unicore32-softmmu.mak b/default-configs/unicore32-softmmu.mak new file mode 100644 index 0000000000..de38577a35 --- /dev/null +++ b/default-configs/unicore32-softmmu.mak @@ -0,0 +1,4 @@ +# Default configuration for unicore32-softmmu +CONFIG_PUV3=y +CONFIG_PTIMER=y +CONFIG_PCKBD=y diff --git a/dma-helpers.c b/dma-helpers.c index 35cb500581..13593d1b42 100644 --- a/dma-helpers.c +++ b/dma-helpers.c @@ -65,6 +65,7 @@ void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len) void qemu_sglist_destroy(QEMUSGList *qsg) { g_free(qsg->sg); + memset(qsg, 0, sizeof(*qsg)); } typedef struct { diff --git a/docs/specs/qcow2.txt b/docs/specs/qcow2.txt index 87bf785fe0..36a559d886 100644 --- a/docs/specs/qcow2.txt +++ b/docs/specs/qcow2.txt @@ -75,13 +75,23 @@ in the description of a field. Bitmask of incompatible features. An implementation must fail to open an image if an unknown bit is set. - Bits 0-63: Reserved (set to 0) + Bit 0: Dirty bit. If this bit is set then refcounts + may be inconsistent, make sure to scan L1/L2 + tables to repair refcounts before accessing the + image. + + Bits 1-63: Reserved (set to 0) 80 - 87: compatible_features Bitmask of compatible features. An implementation can safely ignore any unknown bits that are set. - Bits 0-63: Reserved (set to 0) + Bit 0: Lazy refcounts bit. If this bit is set then + lazy refcount updates can be used. This means + marking the image file dirty and postponing + refcount metadata updates. + + Bits 1-63: Reserved (set to 0) 88 - 95: autoclear_features Bitmask of auto-clear features. An implementation may only diff --git a/docs/writing-qmp-commands.txt b/docs/writing-qmp-commands.txt index 0ad51aa22a..8349dec8af 100644 --- a/docs/writing-qmp-commands.txt +++ b/docs/writing-qmp-commands.txt @@ -210,19 +210,17 @@ 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. +file. Basically, errors are set by calling the error_set() function. 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: +it does contain it, we want the "hello-world" command to return an error: 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"); + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "the word 'love' is not allowed"); return; } printf("%s\n", message); @@ -231,30 +229,40 @@ void qmp_hello_world(bool has_message, const char *message, Error **errp) } } -Let's test it. Build qemu, run it as defined in the "Testing" section, and -then issue the following command: +The first argument to the error_set() function is the Error pointer to pointer, +which is passed to all QMP functions. The second argument is a ErrorClass +value, which should be ERROR_CLASS_GENERIC_ERROR most of the time (more +details about error classes are given below). The third argument is a human +description of the error, this is a free-form printf-like string. + +Let's test the example above. Build qemu, run it as defined in the "Testing" +section, and then issue the following command: -{ "execute": "hello-world", "arguments": { "message": "we love qemu" } } +{ "execute": "hello-world", "arguments": { "message": "all you need is love" } } The QMP server's response should be: { "error": { - "class": "InvalidParameter", - "desc": "Invalid parameter 'message'", - "data": { - "name": "message" - } + "class": "GenericError", + "desc": "the word 'love' is not allowed" } } -Which is the InvalidParameter error. +As a general rule, all QMP errors should use ERROR_CLASS_GENERIC_ERROR. There +are two exceptions to this rule: + + 1. A non-generic ErrorClass value exists* for the failure you want to report + (eg. DeviceNotFound) + + 2. Management applications have to take special action on the failure you + want to report, hence you have to add a new ErrorClass value so that they + can check for it -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. +If the failure you want to report doesn't fall in one of the two cases above, +just report ERROR_CLASS_GENERIC_ERROR. -FIXME: describe better the error API and how to add new errors. + * All existing ErrorClass values are defined in the qapi-schema.json file === Command Documentation === @@ -275,7 +283,6 @@ here goes "hello-world"'s new entry for the qapi-schema.json file: # @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 diff --git a/docs/xbzrle.txt b/docs/xbzrle.txt new file mode 100644 index 0000000000..cc3a26a91d --- /dev/null +++ b/docs/xbzrle.txt @@ -0,0 +1,128 @@ +XBZRLE (Xor Based Zero Run Length Encoding) +=========================================== + +Using XBZRLE (Xor Based Zero Run Length Encoding) allows for the reduction +of VM downtime and the total live-migration time of Virtual machines. +It is particularly useful for virtual machines running memory write intensive +workloads that are typical of large enterprise applications such as SAP ERP +Systems, and generally speaking for any application that uses a sparse memory +update pattern. + +Instead of sending the changed guest memory page this solution will send a +compressed version of the updates, thus reducing the amount of data sent during +live migration. +In order to be able to calculate the update, the previous memory pages need to +be stored on the source. Those pages are stored in a dedicated cache +(hash table) and are accessed by their address. +The larger the cache size the better the chances are that the page has already +been stored in the cache. +A small cache size will result in high cache miss rate. +Cache size can be changed before and during migration. + +Format +======= + +The compression format performs a XOR between the previous and current content +of the page, where zero represents an unchanged value. +The page data delta is represented by zero and non zero runs. +A zero run is represented by its length (in bytes). +A non zero run is represented by its length (in bytes) and the new data. +The run length is encoded using ULEB128 (http://en.wikipedia.org/wiki/LEB128) + +There can be more than one valid encoding, the sender may send a longer encoding +for the benefit of reducing computation cost. + +page = zrun nzrun + | zrun nzrun page + +zrun = length + +nzrun = length byte... + +length = uleb128 encoded integer + +On the sender side XBZRLE is used as a compact delta encoding of page updates, +retrieving the old page content from the cache (default size of 512 MB). The +receiving side uses the existing page's content and XBZRLE to decode the new +page's content. + +This work was originally based on research results published +VEE 2011: Evaluation of Delta Compression Techniques for Efficient Live +Migration of Large Virtual Machines by Benoit, Svard, Tordsson and Elmroth. +Additionally the delta encoder XBRLE was improved further using the XBZRLE +instead. + +XBZRLE has a sustained bandwidth of 2-2.5 GB/s for typical workloads making it +ideal for in-line, real-time encoding such as is needed for live-migration. + +Example +old buffer: +1001 zeros +05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 68 00 00 6b 00 6d +3074 zeros + +new buffer: +1001 zeros +01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 68 00 00 67 00 69 +3074 zeros + +encoded buffer: + +encoded length 24 +e9 07 0f 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 03 01 67 01 01 69 + +Usage +====================== +1. Verify the destination QEMU version is able to decode the new format. + {qemu} info migrate_capabilities + {qemu} xbzrle: off , ... + +2. Activate xbzrle on both source and destination: + {qemu} migrate_set_capability xbzrle on + +3. Set the XBZRLE cache size - the cache size is in MBytes and should be a +power of 2. The cache default value is 64MBytes. (on source only) + {qemu} migrate_set_cache_size 256m + +4. Start outgoing migration + {qemu} migrate -d tcp:destination.host:4444 + {qemu} info migrate + capabilities: xbzrle: on + Migration status: active + transferred ram: A kbytes + remaining ram: B kbytes + total ram: C kbytes + total time: D milliseconds + duplicate: E pages + normal: F pages + normal bytes: G kbytes + cache size: H bytes + xbzrle transferred: I kbytes + xbzrle pages: J pages + xbzrle cache miss: K + xbzrle overflow : L + +xbzrle cache-miss: the number of cache misses to date - high cache-miss rate +indicates that the cache size is set too low. +xbzrle overflow: the number of overflows in the decoding which where the delta +could not be compressed. This can happen if the changes in the pages are too +large or there are many short changes; for example, changing every second byte +(half a page). + +Testing: Testing indicated that live migration with XBZRLE was completed in 110 +seconds, whereas without it would not be able to complete. + +A simple synthetic memory r/w load generator: +.. include <stdlib.h> +.. include <stdio.h> +.. int main() +.. { +.. char *buf = (char *) calloc(4096, 4096); +.. while (1) { +.. int i; +.. for (i = 0; i < 4096 * 4; i++) { +.. buf[i * 4096 / 4]++; +.. } +.. printf("."); +.. } +.. } diff --git a/error.c b/error.c index 58f55a012e..1f05fc466e 100644 --- a/error.c +++ b/error.c @@ -14,17 +14,16 @@ #include "error.h" #include "qjson.h" #include "qdict.h" -#include "error_int.h" +#include "qapi-types.h" #include "qerror.h" struct Error { - QDict *obj; - const char *fmt; char *msg; + ErrorClass err_class; }; -void error_set(Error **errp, const char *fmt, ...) +void error_set(Error **errp, ErrorClass err_class, const char *fmt, ...) { Error *err; va_list ap; @@ -37,9 +36,9 @@ void error_set(Error **errp, const char *fmt, ...) err = g_malloc0(sizeof(*err)); va_start(ap, fmt); - err->obj = qobject_to_qdict(qobject_from_jsonv(fmt, &ap)); + err->msg = g_strdup_vprintf(fmt, ap); va_end(ap); - err->fmt = fmt; + err->err_class = err_class; *errp = err; } @@ -50,9 +49,7 @@ Error *error_copy(const Error *err) err_new = g_malloc0(sizeof(*err)); err_new->msg = g_strdup(err->msg); - err_new->fmt = err->fmt; - err_new->obj = err->obj; - QINCREF(err_new->obj); + err_new->err_class = err->err_class; return err_new; } @@ -62,75 +59,24 @@ bool error_is_set(Error **errp) return (errp && *errp); } -const char *error_get_pretty(Error *err) -{ - if (err->msg == NULL) { - QString *str; - str = qerror_format(err->fmt, err->obj); - err->msg = g_strdup(qstring_get_str(str)); - QDECREF(str); - } - - return err->msg; -} - -const char *error_get_field(Error *err, const char *field) -{ - if (strcmp(field, "class") == 0) { - return qdict_get_str(err->obj, field); - } else { - QDict *dict = qdict_get_qdict(err->obj, "data"); - return qdict_get_str(dict, field); - } -} - -QDict *error_get_data(Error *err) +ErrorClass error_get_class(const Error *err) { - QDict *data = qdict_get_qdict(err->obj, "data"); - QINCREF(data); - return data; + return err->err_class; } -void error_set_field(Error *err, const char *field, const char *value) +const char *error_get_pretty(Error *err) { - QDict *dict = qdict_get_qdict(err->obj, "data"); - qdict_put(dict, field, qstring_from_str(value)); + return err->msg; } void error_free(Error *err) { if (err) { - QDECREF(err->obj); g_free(err->msg); g_free(err); } } -bool error_is_type(Error *err, const char *fmt) -{ - const char *error_class; - char *ptr; - char *end; - - if (!err) { - return false; - } - - ptr = strstr(fmt, "'class': '"); - assert(ptr != NULL); - ptr += strlen("'class': '"); - - end = strchr(ptr, '\''); - assert(end != NULL); - - error_class = error_get_field(err, "class"); - if (strlen(error_class) != end - ptr) { - return false; - } - - return strncmp(ptr, error_class, end - ptr) == 0; -} - void error_propagate(Error **dst_err, Error *local_err) { if (dst_err && !*dst_err) { @@ -139,22 +85,3 @@ void error_propagate(Error **dst_err, Error *local_err) error_free(local_err); } } - -QObject *error_get_qobject(Error *err) -{ - QINCREF(err->obj); - return QOBJECT(err->obj); -} - -void error_set_qobject(Error **errp, QObject *obj) -{ - Error *err; - if (errp == NULL) { - return; - } - err = g_malloc0(sizeof(*err)); - err->obj = qobject_to_qdict(obj); - qobject_incref(obj); - - *errp = err; -} diff --git a/error.h b/error.h index 3d9d96def0..96fc20328f 100644 --- a/error.h +++ b/error.h @@ -13,20 +13,21 @@ #define ERROR_H #include "compiler.h" +#include "qapi-types.h" #include <stdbool.h> /** - * A class representing internal errors within QEMU. An error has a string - * typename and optionally a set of named string parameters. + * A class representing internal errors within QEMU. An error has a ErrorClass + * code and a human message. */ typedef struct Error Error; /** - * Set an indirect pointer to an error given a printf-style format parameter. - * Currently, qerror.h defines these error formats. This function is not - * meant to be used outside of QEMU. + * Set an indirect pointer to an error given a ErrorClass value and a + * printf-style human message. This function is not meant to be used outside + * of QEMU. */ -void error_set(Error **err, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +void error_set(Error **err, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(3, 4); /** * Returns true if an indirect pointer to an error is pointing to a valid @@ -34,6 +35,11 @@ void error_set(Error **err, const char *fmt, ...) GCC_FMT_ATTR(2, 3); */ bool error_is_set(Error **err); +/* + * Get the error class of an error object. + */ +ErrorClass error_get_class(const Error *err); + /** * Returns an exact copy of the error passed as an argument. */ @@ -45,16 +51,6 @@ Error *error_copy(const Error *err); const char *error_get_pretty(Error *err); /** - * Get an individual named error field. - */ -const char *error_get_field(Error *err, const char *field); - -/** - * Get an individual named error field. - */ -void error_set_field(Error *err, const char *field, const char *value); - -/** * Propagate an error to an indirect pointer to an error. This function will * always transfer ownership of the error reference and handles the case where * dst_err is NULL correctly. Errors after the first are discarded. @@ -66,10 +62,4 @@ void error_propagate(Error **dst_err, Error *local_err); */ void error_free(Error *err); -/** - * Determine if an error is of a speific type (based on the qerror format). - * Non-QEMU users should get the `class' field to identify the error type. - */ -bool error_is_type(Error *err, const char *fmt); - #endif diff --git a/error_int.h b/error_int.h deleted file mode 100644 index 5e3942405a..0000000000 --- a/error_int.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * QEMU Error Objects - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2. See - * the COPYING.LIB file in the top-level directory. - */ -#ifndef QEMU_ERROR_INT_H -#define QEMU_ERROR_INT_H - -#include "qemu-common.h" -#include "qobject.h" -#include "qdict.h" -#include "error.h" - -/** - * Internal QEMU functions for working with Error. - * - * These are used to convert QErrors to Errors - */ -QDict *error_get_data(Error *err); -QObject *error_get_qobject(Error *err); -void error_set_qobject(Error **errp, QObject *obj); - -#endif diff --git a/exec-all.h b/exec-all.h index 9bda7f7354..c5ec8e1158 100644 --- a/exec-all.h +++ b/exec-all.h @@ -357,7 +357,7 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr); typedef void (CPUDebugExcpHandler)(CPUArchState *env); -CPUDebugExcpHandler *cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler); +void cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler); /* vl.c */ extern int singlestep; diff --git a/exec.c b/exec.c index a42a0b5b78..929db5cf0a 100644 --- a/exec.c +++ b/exec.c @@ -2550,6 +2550,8 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, ram_list.phys_dirty = g_realloc(ram_list.phys_dirty, last_ram_offset() >> TARGET_PAGE_BITS); + memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS), + 0, size >> TARGET_PAGE_BITS); cpu_physical_memory_set_dirty_range(new_block->offset, size, 0xff); if (kvm_enabled()) diff --git a/hmp-commands.hx b/hmp-commands.hx index eea8b32894..f6104b099f 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -101,7 +101,7 @@ ETEXI .name = "block_job_cancel", .args_type = "device:B", .params = "device", - .help = "stop an active block streaming operation", + .help = "stop an active background block operation", .mhandler.cmd = hmp_block_job_cancel, }, @@ -829,6 +829,26 @@ STEXI @item migrate_cancel @findex migrate_cancel Cancel the current VM migration. + +ETEXI + + { + .name = "migrate_set_cache_size", + .args_type = "value:o", + .params = "value", + .help = "set cache size (in bytes) for XBZRLE migrations," + "the cache size will be rounded down to the nearest " + "power of 2.\n" + "The cache size affects the number of cache misses." + "In case of a high cache miss ratio you need to increase" + " the cache size", + .mhandler.cmd = hmp_migrate_set_cache_size, + }, + +STEXI +@item migrate_set_cache_size @var{value} +@findex migrate_set_cache_size +Set cache size to @var{value} (in bytes) for xbzrle migrations. ETEXI { @@ -861,6 +881,20 @@ Set maximum tolerated downtime (in seconds) for migration. ETEXI { + .name = "migrate_set_capability", + .args_type = "capability:s,state:b", + .params = "capability state", + .help = "Enable/Disable the usage of a capability for migration", + .mhandler.cmd = hmp_migrate_set_capability, + }, + +STEXI +@item migrate_set_capability @var{capability} @var{state} +@findex migrate_set_capability +Enable/Disable the usage of a capability @var{capability} for migration. +ETEXI + + { .name = "client_migrate_info", .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?", .params = "protocol hostname port tls-port cert-subject", @@ -1417,6 +1451,10 @@ show CPU statistics show user network stack connection states @item info migrate show migration status +@item info migrate_capabilities +show current migration capabilities +@item info migrate_cache_size +show current migration XBZRLE cache size @item info balloon show balloon information @item info qtree diff --git a/hmp.c b/hmp.c index 25688ab0e2..a9d5675fc8 100644 --- a/hmp.c +++ b/hmp.c @@ -131,8 +131,21 @@ void hmp_info_mice(Monitor *mon) void hmp_info_migrate(Monitor *mon) { MigrationInfo *info; + MigrationCapabilityStatusList *caps, *cap; info = qmp_query_migrate(NULL); + caps = qmp_query_migrate_capabilities(NULL); + + /* do not display parameters during setup */ + if (info->has_status && caps) { + monitor_printf(mon, "capabilities: "); + for (cap = caps; cap; cap = cap->next) { + monitor_printf(mon, "%s: %s ", + MigrationCapability_lookup[cap->value->capability], + cap->value->state ? "on" : "off"); + } + monitor_printf(mon, "\n"); + } if (info->has_status) { monitor_printf(mon, "Migration status: %s\n", info->status); @@ -147,6 +160,12 @@ void hmp_info_migrate(Monitor *mon) info->ram->total >> 10); monitor_printf(mon, "total time: %" PRIu64 " milliseconds\n", info->ram->total_time); + monitor_printf(mon, "duplicate: %" PRIu64 " pages\n", + info->ram->duplicate); + monitor_printf(mon, "normal: %" PRIu64 " pages\n", + info->ram->normal); + monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n", + info->ram->normal_bytes >> 10); } if (info->has_disk) { @@ -158,7 +177,46 @@ void hmp_info_migrate(Monitor *mon) info->disk->total >> 10); } + if (info->has_xbzrle_cache) { + monitor_printf(mon, "cache size: %" PRIu64 " bytes\n", + info->xbzrle_cache->cache_size); + monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n", + info->xbzrle_cache->bytes >> 10); + monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n", + info->xbzrle_cache->pages); + monitor_printf(mon, "xbzrle cache miss: %" PRIu64 "\n", + info->xbzrle_cache->cache_miss); + monitor_printf(mon, "xbzrle overflow : %" PRIu64 "\n", + info->xbzrle_cache->overflow); + } + qapi_free_MigrationInfo(info); + qapi_free_MigrationCapabilityStatusList(caps); +} + +void hmp_info_migrate_capabilities(Monitor *mon) +{ + MigrationCapabilityStatusList *caps, *cap; + + caps = qmp_query_migrate_capabilities(NULL); + + if (caps) { + monitor_printf(mon, "capabilities: "); + for (cap = caps; cap; cap = cap->next) { + monitor_printf(mon, "%s: %s ", + MigrationCapability_lookup[cap->value->capability], + cap->value->state ? "on" : "off"); + } + monitor_printf(mon, "\n"); + } + + qapi_free_MigrationCapabilityStatusList(caps); +} + +void hmp_info_migrate_cache_size(Monitor *mon) +{ + monitor_printf(mon, "xbzrel cache size: %" PRId64 " kbytes\n", + qmp_query_migrate_cache_size(NULL) >> 10); } void hmp_info_cpus(Monitor *mon) @@ -612,34 +670,35 @@ void hmp_pmemsave(Monitor *mon, const QDict *qdict) static void hmp_cont_cb(void *opaque, int err) { - Monitor *mon = opaque; - if (!err) { - hmp_cont(mon, NULL); + qmp_cont(NULL); } } +static bool key_is_missing(const BlockInfo *bdev) +{ + return (bdev->inserted && bdev->inserted->encryption_key_missing); +} + void hmp_cont(Monitor *mon, const QDict *qdict) { + BlockInfoList *bdev_list, *bdev; 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; + bdev_list = qmp_query_block(NULL); + for (bdev = bdev_list; bdev; bdev = bdev->next) { + if (key_is_missing(bdev->value)) { + monitor_read_block_device_key(mon, bdev->value->device, + hmp_cont_cb, NULL); + goto out; } - hmp_handle_error(mon, &errp); } + + qmp_cont(&errp); + hmp_handle_error(mon, &errp); + +out: + qapi_free_BlockInfoList(bdev_list); } void hmp_system_wakeup(Monitor *mon, const QDict *qdict) @@ -731,12 +790,57 @@ void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict) qmp_migrate_set_downtime(value, NULL); } +void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict) +{ + int64_t value = qdict_get_int(qdict, "value"); + Error *err = NULL; + + qmp_migrate_set_cache_size(value, &err); + if (err) { + monitor_printf(mon, "%s\n", error_get_pretty(err)); + error_free(err); + return; + } +} + void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict) { int64_t value = qdict_get_int(qdict, "value"); qmp_migrate_set_speed(value, NULL); } +void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict) +{ + const char *cap = qdict_get_str(qdict, "capability"); + bool state = qdict_get_bool(qdict, "state"); + Error *err = NULL; + MigrationCapabilityStatusList *caps = g_malloc0(sizeof(*caps)); + int i; + + for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) { + if (strcmp(cap, MigrationCapability_lookup[i]) == 0) { + caps->value = g_malloc0(sizeof(*caps->value)); + caps->value->capability = i; + caps->value->state = state; + caps->next = NULL; + qmp_migrate_set_capabilities(caps, &err); + break; + } + } + + if (i == MIGRATION_CAPABILITY_MAX) { + error_set(&err, QERR_INVALID_PARAMETER, cap); + } + + qapi_free_MigrationCapabilityStatusList(caps); + + if (err) { + monitor_printf(mon, "migrate_set_parameter: %s\n", + error_get_pretty(err)); + error_free(err); + } +} + void hmp_set_password(Monitor *mon, const QDict *qdict) { const char *protocol = qdict_get_str(qdict, "protocol"); @@ -775,22 +879,6 @@ static void hmp_change_read_arg(Monitor *mon, const char *password, monitor_read_command(mon, 1); } -static void cb_hmp_change_bdrv_pwd(Monitor *mon, const char *password, - void *opaque) -{ - Error *encryption_err = opaque; - Error *err = NULL; - const char *device; - - device = error_get_field(encryption_err, "device"); - - qmp_block_passwd(device, password, &err); - hmp_handle_error(mon, &err); - error_free(encryption_err); - - monitor_read_command(mon, 1); -} - void hmp_change(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_str(qdict, "device"); @@ -808,18 +896,10 @@ void hmp_change(Monitor *mon, const QDict *qdict) } qmp_change(device, target, !!arg, arg, &err); - if (error_is_type(err, QERR_DEVICE_ENCRYPTED)) { - monitor_printf(mon, "%s (%s) is encrypted.\n", - error_get_field(err, "device"), - error_get_field(err, "filename")); - if (!monitor_get_rs(mon)) { - monitor_printf(mon, - "terminal does not support password prompting\n"); - error_free(err); - return; - } - readline_start(monitor_get_rs(mon), "Password: ", 1, - cb_hmp_change_bdrv_pwd, err); + if (error_is_set(&err) && + error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) { + error_free(err); + monitor_read_block_device_key(mon, device, NULL, NULL); return; } hmp_handle_error(mon, &err); diff --git a/hmp.h b/hmp.h index 8d2b0d76da..7dd93bf0f4 100644 --- a/hmp.h +++ b/hmp.h @@ -16,6 +16,7 @@ #include "qemu-common.h" #include "qapi-types.h" +#include "qdict.h" void hmp_info_name(Monitor *mon); void hmp_info_version(Monitor *mon); @@ -25,6 +26,8 @@ void hmp_info_uuid(Monitor *mon); void hmp_info_chardev(Monitor *mon); void hmp_info_mice(Monitor *mon); void hmp_info_migrate(Monitor *mon); +void hmp_info_migrate_capabilities(Monitor *mon); +void hmp_info_migrate_cache_size(Monitor *mon); void hmp_info_cpus(Monitor *mon); void hmp_info_block(Monitor *mon); void hmp_info_blockstats(Monitor *mon); @@ -51,6 +54,8 @@ 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); +void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict); +void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict); void hmp_set_password(Monitor *mon, const QDict *qdict); void hmp_expire_password(Monitor *mon, const QDict *qdict); void hmp_eject(Monitor *mon, const QDict *qdict); diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 8327e55c03..7f57ed58e2 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -65,6 +65,34 @@ hw-obj-$(CONFIG_XILINX) += xilinx_timer.o hw-obj-$(CONFIG_XILINX) += xilinx_uartlite.o hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o +hw-obj-$(CONFIG_XILINX_AXI) += stream.o + +# PKUnity SoC devices +hw-obj-$(CONFIG_PUV3) += puv3_intc.o +hw-obj-$(CONFIG_PUV3) += puv3_ost.o +hw-obj-$(CONFIG_PUV3) += puv3_gpio.o +hw-obj-$(CONFIG_PUV3) += puv3_pm.o +hw-obj-$(CONFIG_PUV3) += puv3_dma.o + +# ARM devices +hw-obj-$(CONFIG_ARM_TIMER) += arm_timer.o +hw-obj-$(CONFIG_PL011) += pl011.o +hw-obj-$(CONFIG_PL022) += pl022.o +hw-obj-$(CONFIG_PL031) += pl031.o +hw-obj-$(CONFIG_PL041) += pl041.o lm4549.o +hw-obj-$(CONFIG_PL050) += pl050.o +hw-obj-$(CONFIG_PL061) += pl061.o +hw-obj-$(CONFIG_PL080) += pl080.o +hw-obj-$(CONFIG_PL110) += pl110.o +hw-obj-$(CONFIG_PL181) += pl181.o +hw-obj-$(CONFIG_PL190) += pl190.o +hw-obj-$(CONFIG_PL310) += arm_l2x0.o +hw-obj-$(CONFIG_VERSATILE_PCI) += versatile_pci.o +hw-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o +hw-obj-$(CONFIG_CADENCE) += cadence_uart.o +hw-obj-$(CONFIG_CADENCE) += cadence_ttc.o +hw-obj-$(CONFIG_CADENCE) += cadence_gem.o +hw-obj-$(CONFIG_XGMAC) += xgmac.o # PCI watchdog devices hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o @@ -88,6 +116,7 @@ hw-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o hw-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o hw-obj-$(CONFIG_ESP) += esp.o +hw-obj-$(CONFIG_ESP_PCI) += esp-pci.o hw-obj-y += sysbus.o isa-bus.o hw-obj-y += qdev-addr.o diff --git a/hw/acpi.c b/hw/acpi.c index effc7ec23e..f7950be267 100644 --- a/hw/acpi.c +++ b/hw/acpi.c @@ -22,6 +22,7 @@ #include "hw.h" #include "pc.h" #include "acpi.h" +#include "monitor.h" struct acpi_table_header { uint16_t _length; /* our length, not actual part of the hdr */ @@ -386,6 +387,7 @@ void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val, char s4) break; default: if (sus_typ == s4) { /* S4 request */ + monitor_protocol_event(QEVENT_SUSPEND_DISK, NULL); qemu_system_shutdown_request(); } break; diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index c413780784..2b39fb3c85 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,10 +1,5 @@ -obj-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o -obj-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o -obj-y += versatile_pci.o -obj-y += versatile_i2c.o -obj-y += cadence_uart.o -obj-y += cadence_ttc.o -obj-y += cadence_gem.o +obj-y = integratorcp.o versatilepb.o arm_pic.o +obj-y += arm_boot.o obj-y += xilinx_zynq.o zynq_slcr.o obj-y += arm_gic.o arm_gic_common.o obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o @@ -12,12 +7,9 @@ obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o obj-y += exynos4210_rtc.o exynos4210_i2c.o -obj-y += arm_l2x0.o obj-y += arm_mptimer.o a15mpcore.o -obj-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o +obj-y += armv7m.o armv7m_nvic.o stellaris.o stellaris_enet.o obj-y += highbank.o -obj-y += pl061.o -obj-y += xgmac.o obj-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o obj-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o obj-y += gumstix.o @@ -37,7 +29,6 @@ obj-y += strongarm.o obj-y += collie.o obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o obj-y += kzm.o -obj-y += pl041.o lm4549.o obj-$(CONFIG_FDT) += ../device_tree.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/armv7m.c b/hw/armv7m.c index 8cec78db96..9f66667c6d 100644 --- a/hw/armv7m.c +++ b/hw/armv7m.c @@ -227,6 +227,11 @@ qemu_irq *armv7m_init(MemoryRegion *address_space_mem, big_endian = 0; #endif + if (!kernel_filename) { + fprintf(stderr, "Guest image must be specified (using -kernel)\n"); + exit(1); + } + image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, NULL, big_endian, ELF_MACHINE, 1); if (image_size < 0) { diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 4867c1d5fa..6a0832eb3f 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -467,7 +467,7 @@ static int armv7m_nvic_init(SysBusDevice *dev) s->gic.num_cpu = 1; /* Tell the common code we're an NVIC */ s->gic.revision = 0xffffffff; - s->gic.num_irq = s->num_irq; + s->num_irq = s->gic.num_irq; nc->parent_init(dev); gic_init_irqs_and_distributor(&s->gic, s->num_irq); /* The NVIC and system controller register area looks like this: @@ -498,14 +498,21 @@ static int armv7m_nvic_init(SysBusDevice *dev) return 0; } -static Property armv7m_nvic_properties[] = { +static void armv7m_nvic_instance_init(Object *obj) +{ + /* We have a different default value for the num-irq property + * than our superclass. This function runs after qdev init + * has set the defaults from the Property array and before + * any user-specified property setting, so just modify the + * value in the gic_state struct. + */ + gic_state *s = ARM_GIC_COMMON(obj); /* The ARM v7m may have anything from 0 to 496 external interrupt * IRQ lines. We default to 64. Other boards may differ and should - * set this property appropriately. + * set the num-irq property appropriately. */ - DEFINE_PROP_UINT32("num-irq", nvic_state, num_irq, 64), - DEFINE_PROP_END_OF_LIST(), -}; + s->num_irq = 64; +} static void armv7m_nvic_class_init(ObjectClass *klass, void *data) { @@ -518,12 +525,12 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data) sdc->init = armv7m_nvic_init; dc->vmsd = &vmstate_nvic; dc->reset = armv7m_nvic_reset; - dc->props = armv7m_nvic_properties; } static TypeInfo armv7m_nvic_info = { .name = TYPE_NVIC, .parent = TYPE_ARM_GIC_COMMON, + .instance_init = armv7m_nvic_instance_init, .instance_size = sizeof(nvic_state), .class_init = armv7m_nvic_class_init, .class_size = sizeof(NVICClass), diff --git a/hw/esp-pci.c b/hw/esp-pci.c new file mode 100644 index 0000000000..170e007be9 --- /dev/null +++ b/hw/esp-pci.c @@ -0,0 +1,518 @@ +/* + * QEMU ESP/NCR53C9x emulation + * + * Copyright (c) 2005-2006 Fabrice Bellard + * Copyright (c) 2012 Herve Poussineau + * + * 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 "pci.h" +#include "eeprom93xx.h" +#include "esp.h" +#include "trace.h" +#include "qemu-log.h" + +#define TYPE_AM53C974_DEVICE "am53c974" + +#define DMA_CMD 0x0 +#define DMA_STC 0x1 +#define DMA_SPA 0x2 +#define DMA_WBC 0x3 +#define DMA_WAC 0x4 +#define DMA_STAT 0x5 +#define DMA_SMDLA 0x6 +#define DMA_WMAC 0x7 + +#define DMA_CMD_MASK 0x03 +#define DMA_CMD_DIAG 0x04 +#define DMA_CMD_MDL 0x10 +#define DMA_CMD_INTE_P 0x20 +#define DMA_CMD_INTE_D 0x40 +#define DMA_CMD_DIR 0x80 + +#define DMA_STAT_PWDN 0x01 +#define DMA_STAT_ERROR 0x02 +#define DMA_STAT_ABORT 0x04 +#define DMA_STAT_DONE 0x08 +#define DMA_STAT_SCSIINT 0x10 +#define DMA_STAT_BCMBLT 0x20 + +#define SBAC_STATUS 0x1000 + +typedef struct PCIESPState { + PCIDevice dev; + MemoryRegion io; + uint32_t dma_regs[8]; + uint32_t sbac; + ESPState esp; +} PCIESPState; + +static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val) +{ + trace_esp_pci_dma_idle(val); + esp_dma_enable(&pci->esp, 0, 0); +} + +static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val) +{ + trace_esp_pci_dma_blast(val); + qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n"); +} + +static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val) +{ + trace_esp_pci_dma_abort(val); + if (pci->esp.current_req) { + scsi_req_cancel(pci->esp.current_req); + } +} + +static void esp_pci_handle_start(PCIESPState *pci, uint32_t val) +{ + trace_esp_pci_dma_start(val); + + pci->dma_regs[DMA_WBC] = pci->dma_regs[DMA_STC]; + pci->dma_regs[DMA_WAC] = pci->dma_regs[DMA_SPA]; + pci->dma_regs[DMA_WMAC] = pci->dma_regs[DMA_SMDLA]; + + pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT + | DMA_STAT_DONE | DMA_STAT_ABORT + | DMA_STAT_ERROR | DMA_STAT_PWDN); + + esp_dma_enable(&pci->esp, 0, 1); +} + +static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val) +{ + trace_esp_pci_dma_write(saddr, pci->dma_regs[saddr], val); + switch (saddr) { + case DMA_CMD: + pci->dma_regs[saddr] = val; + switch (val & DMA_CMD_MASK) { + case 0x0: /* IDLE */ + esp_pci_handle_idle(pci, val); + break; + case 0x1: /* BLAST */ + esp_pci_handle_blast(pci, val); + break; + case 0x2: /* ABORT */ + esp_pci_handle_abort(pci, val); + break; + case 0x3: /* START */ + esp_pci_handle_start(pci, val); + break; + default: /* can't happen */ + abort(); + } + break; + case DMA_STC: + case DMA_SPA: + case DMA_SMDLA: + pci->dma_regs[saddr] = val; + break; + case DMA_STAT: + if (!(pci->sbac & SBAC_STATUS)) { + /* clear some bits on write */ + uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE; + pci->dma_regs[DMA_STAT] &= ~(val & mask); + } + break; + default: + trace_esp_pci_error_invalid_write_dma(val, saddr); + return; + } +} + +static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr) +{ + uint32_t val; + + val = pci->dma_regs[saddr]; + if (saddr == DMA_STAT) { + if (pci->esp.rregs[ESP_RSTAT] & STAT_INT) { + val |= DMA_STAT_SCSIINT; + } + if (pci->sbac & SBAC_STATUS) { + pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT | + DMA_STAT_DONE); + } + } + + trace_esp_pci_dma_read(saddr, val); + return val; +} + +static void esp_pci_io_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned int size) +{ + PCIESPState *pci = opaque; + + if (size < 4 || addr & 3) { + /* need to upgrade request: we only support 4-bytes accesses */ + uint32_t current = 0, mask; + int shift; + + if (addr < 0x40) { + current = pci->esp.wregs[addr >> 2]; + } else if (addr < 0x60) { + current = pci->dma_regs[(addr - 0x40) >> 2]; + } else if (addr < 0x74) { + current = pci->sbac; + } + + shift = (4 - size) * 8; + mask = (~(uint32_t)0 << shift) >> shift; + + shift = ((4 - (addr & 3)) & 3) * 8; + val <<= shift; + val |= current & ~(mask << shift); + addr &= ~3; + size = 4; + } + + if (addr < 0x40) { + /* SCSI core reg */ + esp_reg_write(&pci->esp, addr >> 2, val); + } else if (addr < 0x60) { + /* PCI DMA CCB */ + esp_pci_dma_write(pci, (addr - 0x40) >> 2, val); + } else if (addr == 0x70) { + /* DMA SCSI Bus and control */ + trace_esp_pci_sbac_write(pci->sbac, val); + pci->sbac = val; + } else { + trace_esp_pci_error_invalid_write((int)addr); + } +} + +static uint64_t esp_pci_io_read(void *opaque, target_phys_addr_t addr, + unsigned int size) +{ + PCIESPState *pci = opaque; + uint32_t ret; + + if (addr < 0x40) { + /* SCSI core reg */ + ret = esp_reg_read(&pci->esp, addr >> 2); + } else if (addr < 0x60) { + /* PCI DMA CCB */ + ret = esp_pci_dma_read(pci, (addr - 0x40) >> 2); + } else if (addr == 0x70) { + /* DMA SCSI Bus and control */ + trace_esp_pci_sbac_read(pci->sbac); + ret = pci->sbac; + } else { + /* Invalid region */ + trace_esp_pci_error_invalid_read((int)addr); + ret = 0; + } + + /* give only requested data */ + ret >>= (addr & 3) * 8; + ret &= ~(~(uint64_t)0 << (8 * size)); + + return ret; +} + +static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len, + DMADirection dir) +{ + dma_addr_t addr; + DMADirection expected_dir; + + if (pci->dma_regs[DMA_CMD] & DMA_CMD_DIR) { + expected_dir = DMA_DIRECTION_FROM_DEVICE; + } else { + expected_dir = DMA_DIRECTION_TO_DEVICE; + } + + if (dir != expected_dir) { + trace_esp_pci_error_invalid_dma_direction(); + return; + } + + if (pci->dma_regs[DMA_STAT] & DMA_CMD_MDL) { + qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n"); + } + + addr = pci->dma_regs[DMA_SPA]; + if (pci->dma_regs[DMA_WBC] < len) { + len = pci->dma_regs[DMA_WBC]; + } + + pci_dma_rw(&pci->dev, addr, buf, len, dir); + + /* update status registers */ + pci->dma_regs[DMA_WBC] -= len; + pci->dma_regs[DMA_WAC] += len; +} + +static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len) +{ + PCIESPState *pci = opaque; + esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_TO_DEVICE); +} + +static void esp_pci_dma_memory_write(void *opaque, uint8_t *buf, int len) +{ + PCIESPState *pci = opaque; + esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_FROM_DEVICE); +} + +static const MemoryRegionOps esp_pci_io_ops = { + .read = esp_pci_io_read, + .write = esp_pci_io_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void esp_pci_hard_reset(DeviceState *dev) +{ + PCIESPState *pci = DO_UPCAST(PCIESPState, dev.qdev, dev); + esp_hard_reset(&pci->esp); + pci->dma_regs[DMA_CMD] &= ~(DMA_CMD_DIR | DMA_CMD_INTE_D | DMA_CMD_INTE_P + | DMA_CMD_MDL | DMA_CMD_DIAG | DMA_CMD_MASK); + pci->dma_regs[DMA_WBC] &= ~0xffff; + pci->dma_regs[DMA_WAC] = 0xffffffff; + pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT + | DMA_STAT_DONE | DMA_STAT_ABORT + | DMA_STAT_ERROR); + pci->dma_regs[DMA_WMAC] = 0xfffffffd; +} + +static const VMStateDescription vmstate_esp_pci_scsi = { + .name = "pciespscsi", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(dev, PCIESPState), + VMSTATE_BUFFER_UNSAFE(dma_regs, PCIESPState, 0, 8 * sizeof(uint32_t)), + VMSTATE_STRUCT(esp, PCIESPState, 0, vmstate_esp, ESPState), + VMSTATE_END_OF_LIST() + } +}; + +static void esp_pci_command_complete(SCSIRequest *req, uint32_t status, + size_t resid) +{ + ESPState *s = req->hba_private; + PCIESPState *pci = container_of(s, PCIESPState, esp); + + esp_command_complete(req, status, resid); + pci->dma_regs[DMA_WBC] = 0; + pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; +} + +static const struct SCSIBusInfo esp_pci_scsi_info = { + .tcq = false, + .max_target = ESP_MAX_DEVS, + .max_lun = 7, + + .transfer_data = esp_transfer_data, + .complete = esp_pci_command_complete, + .cancel = esp_request_cancelled, +}; + +static int esp_pci_scsi_init(PCIDevice *dev) +{ + PCIESPState *pci = DO_UPCAST(PCIESPState, dev, dev); + ESPState *s = &pci->esp; + uint8_t *pci_conf; + + pci_conf = pci->dev.config; + + /* Interrupt pin A */ + pci_conf[PCI_INTERRUPT_PIN] = 0x01; + + s->dma_memory_read = esp_pci_dma_memory_read; + s->dma_memory_write = esp_pci_dma_memory_write; + s->dma_opaque = pci; + s->chip_id = TCHI_AM53C974; + memory_region_init_io(&pci->io, &esp_pci_io_ops, pci, "esp-io", 0x80); + + pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io); + s->irq = pci->dev.irq[0]; + + scsi_bus_new(&s->bus, &dev->qdev, &esp_pci_scsi_info); + if (!dev->qdev.hotplugged) { + return scsi_bus_legacy_handle_cmdline(&s->bus); + } + return 0; +} + +static void esp_pci_scsi_uninit(PCIDevice *d) +{ + PCIESPState *pci = DO_UPCAST(PCIESPState, dev, d); + + memory_region_destroy(&pci->io); +} + +static void esp_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = esp_pci_scsi_init; + k->exit = esp_pci_scsi_uninit; + k->vendor_id = PCI_VENDOR_ID_AMD; + k->device_id = PCI_DEVICE_ID_AMD_SCSI; + k->revision = 0x10; + k->class_id = PCI_CLASS_STORAGE_SCSI; + dc->desc = "AMD Am53c974 PCscsi-PCI SCSI adapter"; + dc->reset = esp_pci_hard_reset; + dc->vmsd = &vmstate_esp_pci_scsi; +} + +static const TypeInfo esp_pci_info = { + .name = TYPE_AM53C974_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIESPState), + .class_init = esp_pci_class_init, +}; + +typedef struct { + PCIESPState pci; + eeprom_t *eeprom; +} DC390State; + +#define TYPE_DC390_DEVICE "dc390" +#define DC390(obj) \ + OBJECT_CHECK(DC390State, obj, TYPE_DC390_DEVICE) + +#define EE_ADAPT_SCSI_ID 64 +#define EE_MODE2 65 +#define EE_DELAY 66 +#define EE_TAG_CMD_NUM 67 +#define EE_ADAPT_OPTIONS 68 +#define EE_BOOT_SCSI_ID 69 +#define EE_BOOT_SCSI_LUN 70 +#define EE_CHKSUM1 126 +#define EE_CHKSUM2 127 + +#define EE_ADAPT_OPTION_F6_F8_AT_BOOT 0x01 +#define EE_ADAPT_OPTION_BOOT_FROM_CDROM 0x02 +#define EE_ADAPT_OPTION_INT13 0x04 +#define EE_ADAPT_OPTION_SCAM_SUPPORT 0x08 + + +static uint32_t dc390_read_config(PCIDevice *dev, uint32_t addr, int l) +{ + DC390State *pci = DC390(dev); + uint32_t val; + + val = pci_default_read_config(dev, addr, l); + + if (addr == 0x00 && l == 1) { + /* First byte of address space is AND-ed with EEPROM DO line */ + if (!eeprom93xx_read(pci->eeprom)) { + val &= ~0xff; + } + } + + return val; +} + +static void dc390_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int l) +{ + DC390State *pci = DC390(dev); + if (addr == 0x80) { + /* EEPROM write */ + int eesk = val & 0x80 ? 1 : 0; + int eedi = val & 0x40 ? 1 : 0; + eeprom93xx_write(pci->eeprom, 1, eesk, eedi); + } else if (addr == 0xc0) { + /* EEPROM CS low */ + eeprom93xx_write(pci->eeprom, 0, 0, 0); + } else { + pci_default_write_config(dev, addr, val, l); + } +} + +static int dc390_scsi_init(PCIDevice *dev) +{ + DC390State *pci = DC390(dev); + uint8_t *contents; + uint16_t chksum = 0; + int i, ret; + + /* init base class */ + ret = esp_pci_scsi_init(dev); + if (ret < 0) { + return ret; + } + + /* EEPROM */ + pci->eeprom = eeprom93xx_new(DEVICE(dev), 64); + + /* set default eeprom values */ + contents = (uint8_t *)eeprom93xx_data(pci->eeprom); + + for (i = 0; i < 16; i++) { + contents[i * 2] = 0x57; + contents[i * 2 + 1] = 0x00; + } + contents[EE_ADAPT_SCSI_ID] = 7; + contents[EE_MODE2] = 0x0f; + contents[EE_TAG_CMD_NUM] = 0x04; + contents[EE_ADAPT_OPTIONS] = EE_ADAPT_OPTION_F6_F8_AT_BOOT + | EE_ADAPT_OPTION_BOOT_FROM_CDROM + | EE_ADAPT_OPTION_INT13; + + /* update eeprom checksum */ + for (i = 0; i < EE_CHKSUM1; i += 2) { + chksum += contents[i] + (((uint16_t)contents[i + 1]) << 8); + } + chksum = 0x1234 - chksum; + contents[EE_CHKSUM1] = chksum & 0xff; + contents[EE_CHKSUM2] = chksum >> 8; + + return 0; +} + +static void dc390_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = dc390_scsi_init; + k->config_read = dc390_read_config; + k->config_write = dc390_write_config; + dc->desc = "Tekram DC-390 SCSI adapter"; +} + +static const TypeInfo dc390_info = { + .name = "dc390", + .parent = TYPE_AM53C974_DEVICE, + .instance_size = sizeof(DC390State), + .class_init = dc390_class_init, +}; + +static void esp_pci_register_types(void) +{ + type_register_static(&esp_pci_info); + type_register_static(&dc390_info); +} + +type_init(esp_pci_register_types) diff --git a/hw/esp.c b/hw/esp.c index 77f57076c7..52c46e615f 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -24,8 +24,6 @@ */ #include "sysbus.h" -#include "pci.h" -#include "scsi.h" #include "esp.h" #include "trace.h" #include "qemu-log.h" @@ -38,114 +36,6 @@ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt */ -#define ESP_REGS 16 -#define TI_BUFSZ 16 - -typedef struct ESPState ESPState; - -struct ESPState { - uint8_t rregs[ESP_REGS]; - uint8_t wregs[ESP_REGS]; - qemu_irq irq; - uint8_t chip_id; - int32_t ti_size; - uint32_t ti_rptr, ti_wptr; - uint32_t status; - uint32_t dma; - uint8_t ti_buf[TI_BUFSZ]; - SCSIBus bus; - SCSIDevice *current_dev; - SCSIRequest *current_req; - uint8_t cmdbuf[TI_BUFSZ]; - uint32_t cmdlen; - uint32_t do_cmd; - - /* The amount of data left in the current DMA transfer. */ - uint32_t dma_left; - /* The size of the current DMA transfer. Zero if no transfer is in - progress. */ - uint32_t dma_counter; - int dma_enabled; - - uint32_t async_len; - uint8_t *async_buf; - - ESPDMAMemoryReadWriteFunc dma_memory_read; - ESPDMAMemoryReadWriteFunc dma_memory_write; - void *dma_opaque; - void (*dma_cb)(ESPState *s); -}; - -#define ESP_TCLO 0x0 -#define ESP_TCMID 0x1 -#define ESP_FIFO 0x2 -#define ESP_CMD 0x3 -#define ESP_RSTAT 0x4 -#define ESP_WBUSID 0x4 -#define ESP_RINTR 0x5 -#define ESP_WSEL 0x5 -#define ESP_RSEQ 0x6 -#define ESP_WSYNTP 0x6 -#define ESP_RFLAGS 0x7 -#define ESP_WSYNO 0x7 -#define ESP_CFG1 0x8 -#define ESP_RRES1 0x9 -#define ESP_WCCF 0x9 -#define ESP_RRES2 0xa -#define ESP_WTEST 0xa -#define ESP_CFG2 0xb -#define ESP_CFG3 0xc -#define ESP_RES3 0xd -#define ESP_TCHI 0xe -#define ESP_RES4 0xf - -#define CMD_DMA 0x80 -#define CMD_CMD 0x7f - -#define CMD_NOP 0x00 -#define CMD_FLUSH 0x01 -#define CMD_RESET 0x02 -#define CMD_BUSRESET 0x03 -#define CMD_TI 0x10 -#define CMD_ICCS 0x11 -#define CMD_MSGACC 0x12 -#define CMD_PAD 0x18 -#define CMD_SATN 0x1a -#define CMD_RSTATN 0x1b -#define CMD_SEL 0x41 -#define CMD_SELATN 0x42 -#define CMD_SELATNS 0x43 -#define CMD_ENSEL 0x44 -#define CMD_DISSEL 0x45 - -#define STAT_DO 0x00 -#define STAT_DI 0x01 -#define STAT_CD 0x02 -#define STAT_ST 0x03 -#define STAT_MO 0x06 -#define STAT_MI 0x07 -#define STAT_PIO_MASK 0x06 - -#define STAT_TC 0x10 -#define STAT_PE 0x20 -#define STAT_GE 0x40 -#define STAT_INT 0x80 - -#define BUSID_DID 0x07 - -#define INTR_FC 0x08 -#define INTR_BS 0x10 -#define INTR_DC 0x20 -#define INTR_RST 0x80 - -#define SEQ_0 0x0 -#define SEQ_CD 0x4 - -#define CFG1_RESREPT 0x40 - -#define TCHI_FAS100A 0x4 -#define TCHI_AM53C974 0x12 - static void esp_raise_irq(ESPState *s) { if (!(s->rregs[ESP_RSTAT] & STAT_INT)) { @@ -164,7 +54,7 @@ static void esp_lower_irq(ESPState *s) } } -static void esp_dma_enable(ESPState *s, int irq, int level) +void esp_dma_enable(ESPState *s, int irq, int level) { if (level) { s->dma_enabled = 1; @@ -179,7 +69,7 @@ static void esp_dma_enable(ESPState *s, int irq, int level) } } -static void esp_request_cancelled(SCSIRequest *req) +void esp_request_cancelled(SCSIRequest *req) { ESPState *s = req->hba_private; @@ -388,7 +278,7 @@ static void esp_do_dma(ESPState *s) esp_dma_done(s); } -static void esp_command_complete(SCSIRequest *req, uint32_t status, +void esp_command_complete(SCSIRequest *req, uint32_t status, size_t resid) { ESPState *s = req->hba_private; @@ -413,7 +303,7 @@ static void esp_command_complete(SCSIRequest *req, uint32_t status, } } -static void esp_transfer_data(SCSIRequest *req, uint32_t len) +void esp_transfer_data(SCSIRequest *req, uint32_t len) { ESPState *s = req->hba_private; @@ -465,7 +355,7 @@ static void handle_ti(ESPState *s) } } -static void esp_hard_reset(ESPState *s) +void esp_hard_reset(ESPState *s) { memset(s->rregs, 0, ESP_REGS); memset(s->wregs, 0, ESP_REGS); @@ -493,7 +383,7 @@ static void parent_esp_reset(ESPState *s, int irq, int level) } } -static uint64_t esp_reg_read(ESPState *s, uint32_t saddr) +uint64_t esp_reg_read(ESPState *s, uint32_t saddr) { uint32_t old_val; @@ -533,7 +423,7 @@ static uint64_t esp_reg_read(ESPState *s, uint32_t saddr) return s->rregs[saddr]; } -static void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) +void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) { trace_esp_mem_writeb(saddr, s->wregs[saddr], val); switch (saddr) { @@ -660,7 +550,7 @@ static bool esp_mem_accepts(void *opaque, target_phys_addr_t addr, return (size == 1) || (is_write && size == 4); } -static const VMStateDescription vmstate_esp = { +const VMStateDescription vmstate_esp = { .name ="esp", .version_id = 3, .minimum_version_id = 3, @@ -823,370 +713,9 @@ static const TypeInfo sysbus_esp_info = { .class_init = sysbus_esp_class_init, }; -#define DMA_CMD 0x0 -#define DMA_STC 0x1 -#define DMA_SPA 0x2 -#define DMA_WBC 0x3 -#define DMA_WAC 0x4 -#define DMA_STAT 0x5 -#define DMA_SMDLA 0x6 -#define DMA_WMAC 0x7 - -#define DMA_CMD_MASK 0x03 -#define DMA_CMD_DIAG 0x04 -#define DMA_CMD_MDL 0x10 -#define DMA_CMD_INTE_P 0x20 -#define DMA_CMD_INTE_D 0x40 -#define DMA_CMD_DIR 0x80 - -#define DMA_STAT_PWDN 0x01 -#define DMA_STAT_ERROR 0x02 -#define DMA_STAT_ABORT 0x04 -#define DMA_STAT_DONE 0x08 -#define DMA_STAT_SCSIINT 0x10 -#define DMA_STAT_BCMBLT 0x20 - -#define SBAC_STATUS 0x1000 - -typedef struct PCIESPState { - PCIDevice dev; - MemoryRegion io; - uint32_t dma_regs[8]; - uint32_t sbac; - ESPState esp; -} PCIESPState; - -static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_idle(val); - esp_dma_enable(&pci->esp, 0, 0); -} - -static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_blast(val); - qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n"); -} - -static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_abort(val); - if (pci->esp.current_req) { - scsi_req_cancel(pci->esp.current_req); - } -} - -static void esp_pci_handle_start(PCIESPState *pci, uint32_t val) -{ - trace_esp_pci_dma_start(val); - - pci->dma_regs[DMA_WBC] = pci->dma_regs[DMA_STC]; - pci->dma_regs[DMA_WAC] = pci->dma_regs[DMA_SPA]; - pci->dma_regs[DMA_WMAC] = pci->dma_regs[DMA_SMDLA]; - - pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT - | DMA_STAT_DONE | DMA_STAT_ABORT - | DMA_STAT_ERROR | DMA_STAT_PWDN); - - esp_dma_enable(&pci->esp, 0, 1); -} - -static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val) -{ - trace_esp_pci_dma_write(saddr, pci->dma_regs[saddr], val); - switch (saddr) { - case DMA_CMD: - pci->dma_regs[saddr] = val; - switch (val & DMA_CMD_MASK) { - case 0x0: /* IDLE */ - esp_pci_handle_idle(pci, val); - break; - case 0x1: /* BLAST */ - esp_pci_handle_blast(pci, val); - break; - case 0x2: /* ABORT */ - esp_pci_handle_abort(pci, val); - break; - case 0x3: /* START */ - esp_pci_handle_start(pci, val); - break; - default: /* can't happen */ - abort(); - } - break; - case DMA_STC: - case DMA_SPA: - case DMA_SMDLA: - pci->dma_regs[saddr] = val; - break; - case DMA_STAT: - if (!(pci->sbac & SBAC_STATUS)) { - /* clear some bits on write */ - uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE; - pci->dma_regs[DMA_STAT] &= ~(val & mask); - } - break; - default: - trace_esp_pci_error_invalid_write_dma(val, saddr); - return; - } -} - -static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr) -{ - uint32_t val; - - val = pci->dma_regs[saddr]; - if (saddr == DMA_STAT) { - if (pci->esp.rregs[ESP_RSTAT] & STAT_INT) { - val |= DMA_STAT_SCSIINT; - } - if (pci->sbac & SBAC_STATUS) { - pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT | - DMA_STAT_DONE); - } - } - - trace_esp_pci_dma_read(saddr, val); - return val; -} - -static void esp_pci_io_write(void *opaque, target_phys_addr_t addr, - uint64_t val, unsigned int size) -{ - PCIESPState *pci = opaque; - - if (size < 4 || addr & 3) { - /* need to upgrade request: we only support 4-bytes accesses */ - uint32_t current = 0, mask; - int shift; - - if (addr < 0x40) { - current = pci->esp.wregs[addr >> 2]; - } else if (addr < 0x60) { - current = pci->dma_regs[(addr - 0x40) >> 2]; - } else if (addr < 0x74) { - current = pci->sbac; - } - - shift = (4 - size) * 8; - mask = (~(uint32_t)0 << shift) >> shift; - - shift = ((4 - (addr & 3)) & 3) * 8; - val <<= shift; - val |= current & ~(mask << shift); - addr &= ~3; - size = 4; - } - - if (addr < 0x40) { - /* SCSI core reg */ - esp_reg_write(&pci->esp, addr >> 2, val); - } else if (addr < 0x60) { - /* PCI DMA CCB */ - esp_pci_dma_write(pci, (addr - 0x40) >> 2, val); - } else if (addr == 0x70) { - /* DMA SCSI Bus and control */ - trace_esp_pci_sbac_write(pci->sbac, val); - pci->sbac = val; - } else { - trace_esp_pci_error_invalid_write((int)addr); - } -} - -static uint64_t esp_pci_io_read(void *opaque, target_phys_addr_t addr, - unsigned int size) -{ - PCIESPState *pci = opaque; - uint32_t ret; - - if (addr < 0x40) { - /* SCSI core reg */ - ret = esp_reg_read(&pci->esp, addr >> 2); - } else if (addr < 0x60) { - /* PCI DMA CCB */ - ret = esp_pci_dma_read(pci, (addr - 0x40) >> 2); - } else if (addr == 0x70) { - /* DMA SCSI Bus and control */ - trace_esp_pci_sbac_read(pci->sbac); - ret = pci->sbac; - } else { - /* Invalid region */ - trace_esp_pci_error_invalid_read((int)addr); - ret = 0; - } - - /* give only requested data */ - ret >>= (addr & 3) * 8; - ret &= ~(~(uint64_t)0 << (8 * size)); - - return ret; -} - -static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len, - DMADirection dir) -{ - dma_addr_t addr; - DMADirection expected_dir; - - if (pci->dma_regs[DMA_CMD] & DMA_CMD_DIR) { - expected_dir = DMA_DIRECTION_FROM_DEVICE; - } else { - expected_dir = DMA_DIRECTION_TO_DEVICE; - } - - if (dir != expected_dir) { - trace_esp_pci_error_invalid_dma_direction(); - return; - } - - if (pci->dma_regs[DMA_STAT] & DMA_CMD_MDL) { - qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n"); - } - - addr = pci->dma_regs[DMA_SPA]; - if (pci->dma_regs[DMA_WBC] < len) { - len = pci->dma_regs[DMA_WBC]; - } - - pci_dma_rw(&pci->dev, addr, buf, len, dir); - - /* update status registers */ - pci->dma_regs[DMA_WBC] -= len; - pci->dma_regs[DMA_WAC] += len; -} - -static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len) -{ - PCIESPState *pci = opaque; - esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_TO_DEVICE); -} - -static void esp_pci_dma_memory_write(void *opaque, uint8_t *buf, int len) -{ - PCIESPState *pci = opaque; - esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_FROM_DEVICE); -} - -static const MemoryRegionOps esp_pci_io_ops = { - .read = esp_pci_io_read, - .write = esp_pci_io_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 4, - }, -}; - -static void esp_pci_hard_reset(DeviceState *dev) -{ - PCIESPState *pci = DO_UPCAST(PCIESPState, dev.qdev, dev); - esp_hard_reset(&pci->esp); - pci->dma_regs[DMA_CMD] &= ~(DMA_CMD_DIR | DMA_CMD_INTE_D | DMA_CMD_INTE_P - | DMA_CMD_MDL | DMA_CMD_DIAG | DMA_CMD_MASK); - pci->dma_regs[DMA_WBC] &= ~0xffff; - pci->dma_regs[DMA_WAC] = 0xffffffff; - pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT - | DMA_STAT_DONE | DMA_STAT_ABORT - | DMA_STAT_ERROR); - pci->dma_regs[DMA_WMAC] = 0xfffffffd; -} - -static const VMStateDescription vmstate_esp_pci_scsi = { - .name = "pciespscsi", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, PCIESPState), - VMSTATE_BUFFER_UNSAFE(dma_regs, PCIESPState, 0, 8 * sizeof(uint32_t)), - VMSTATE_STRUCT(esp, PCIESPState, 0, vmstate_esp, ESPState), - VMSTATE_END_OF_LIST() - } -}; - -static void esp_pci_command_complete(SCSIRequest *req, uint32_t status, - size_t resid) -{ - ESPState *s = req->hba_private; - PCIESPState *pci = container_of(s, PCIESPState, esp); - - esp_command_complete(req, status, resid); - pci->dma_regs[DMA_WBC] = 0; - pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE; -} - -static const struct SCSIBusInfo esp_pci_scsi_info = { - .tcq = false, - .max_target = ESP_MAX_DEVS, - .max_lun = 7, - - .transfer_data = esp_transfer_data, - .complete = esp_pci_command_complete, - .cancel = esp_request_cancelled, -}; - -static int esp_pci_scsi_init(PCIDevice *dev) -{ - PCIESPState *pci = DO_UPCAST(PCIESPState, dev, dev); - ESPState *s = &pci->esp; - uint8_t *pci_conf; - - pci_conf = pci->dev.config; - - /* Interrupt pin A */ - pci_conf[PCI_INTERRUPT_PIN] = 0x01; - - s->dma_memory_read = esp_pci_dma_memory_read; - s->dma_memory_write = esp_pci_dma_memory_write; - s->dma_opaque = pci; - s->chip_id = TCHI_AM53C974; - memory_region_init_io(&pci->io, &esp_pci_io_ops, pci, "esp-io", 0x80); - - pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io); - s->irq = pci->dev.irq[0]; - - scsi_bus_new(&s->bus, &dev->qdev, &esp_pci_scsi_info); - if (!dev->qdev.hotplugged) { - return scsi_bus_legacy_handle_cmdline(&s->bus); - } - return 0; -} - -static void esp_pci_scsi_uninit(PCIDevice *d) -{ - PCIESPState *pci = DO_UPCAST(PCIESPState, dev, d); - - memory_region_destroy(&pci->io); -} - -static void esp_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = esp_pci_scsi_init; - k->exit = esp_pci_scsi_uninit; - k->vendor_id = PCI_VENDOR_ID_AMD; - k->device_id = PCI_DEVICE_ID_AMD_SCSI; - k->revision = 0x10; - k->class_id = PCI_CLASS_STORAGE_SCSI; - dc->desc = "AMD Am53c974 PCscsi-PCI SCSI adapter"; - dc->reset = esp_pci_hard_reset; - dc->vmsd = &vmstate_esp_pci_scsi; -} - -static const TypeInfo esp_pci_info = { - .name = "am53c974", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIESPState), - .class_init = esp_pci_class_init, -}; - static void esp_register_types(void) { type_register_static(&sysbus_esp_info); - type_register_static(&esp_pci_info); } type_init(esp_register_types) diff --git a/hw/esp.h b/hw/esp.h index 62bfd4d129..fa855e2fdf 100644 --- a/hw/esp.h +++ b/hw/esp.h @@ -1,6 +1,8 @@ #ifndef QEMU_HW_ESP_H #define QEMU_HW_ESP_H +#include "scsi.h" + /* esp.c */ #define ESP_MAX_DEVS 7 typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len); @@ -10,4 +12,121 @@ void esp_init(target_phys_addr_t espaddr, int it_shift, void *dma_opaque, qemu_irq irq, qemu_irq *reset, qemu_irq *dma_enable); +#define ESP_REGS 16 +#define TI_BUFSZ 16 + +typedef struct ESPState ESPState; + +struct ESPState { + uint8_t rregs[ESP_REGS]; + uint8_t wregs[ESP_REGS]; + qemu_irq irq; + uint8_t chip_id; + int32_t ti_size; + uint32_t ti_rptr, ti_wptr; + uint32_t status; + uint32_t dma; + uint8_t ti_buf[TI_BUFSZ]; + SCSIBus bus; + SCSIDevice *current_dev; + SCSIRequest *current_req; + uint8_t cmdbuf[TI_BUFSZ]; + uint32_t cmdlen; + uint32_t do_cmd; + + /* The amount of data left in the current DMA transfer. */ + uint32_t dma_left; + /* The size of the current DMA transfer. Zero if no transfer is in + progress. */ + uint32_t dma_counter; + int dma_enabled; + + uint32_t async_len; + uint8_t *async_buf; + + ESPDMAMemoryReadWriteFunc dma_memory_read; + ESPDMAMemoryReadWriteFunc dma_memory_write; + void *dma_opaque; + void (*dma_cb)(ESPState *s); +}; + +#define ESP_TCLO 0x0 +#define ESP_TCMID 0x1 +#define ESP_FIFO 0x2 +#define ESP_CMD 0x3 +#define ESP_RSTAT 0x4 +#define ESP_WBUSID 0x4 +#define ESP_RINTR 0x5 +#define ESP_WSEL 0x5 +#define ESP_RSEQ 0x6 +#define ESP_WSYNTP 0x6 +#define ESP_RFLAGS 0x7 +#define ESP_WSYNO 0x7 +#define ESP_CFG1 0x8 +#define ESP_RRES1 0x9 +#define ESP_WCCF 0x9 +#define ESP_RRES2 0xa +#define ESP_WTEST 0xa +#define ESP_CFG2 0xb +#define ESP_CFG3 0xc +#define ESP_RES3 0xd +#define ESP_TCHI 0xe +#define ESP_RES4 0xf + +#define CMD_DMA 0x80 +#define CMD_CMD 0x7f + +#define CMD_NOP 0x00 +#define CMD_FLUSH 0x01 +#define CMD_RESET 0x02 +#define CMD_BUSRESET 0x03 +#define CMD_TI 0x10 +#define CMD_ICCS 0x11 +#define CMD_MSGACC 0x12 +#define CMD_PAD 0x18 +#define CMD_SATN 0x1a +#define CMD_RSTATN 0x1b +#define CMD_SEL 0x41 +#define CMD_SELATN 0x42 +#define CMD_SELATNS 0x43 +#define CMD_ENSEL 0x44 +#define CMD_DISSEL 0x45 + +#define STAT_DO 0x00 +#define STAT_DI 0x01 +#define STAT_CD 0x02 +#define STAT_ST 0x03 +#define STAT_MO 0x06 +#define STAT_MI 0x07 +#define STAT_PIO_MASK 0x06 + +#define STAT_TC 0x10 +#define STAT_PE 0x20 +#define STAT_GE 0x40 +#define STAT_INT 0x80 + +#define BUSID_DID 0x07 + +#define INTR_FC 0x08 +#define INTR_BS 0x10 +#define INTR_DC 0x20 +#define INTR_RST 0x80 + +#define SEQ_0 0x0 +#define SEQ_CD 0x4 + +#define CFG1_RESREPT 0x40 + +#define TCHI_FAS100A 0x4 +#define TCHI_AM53C974 0x12 + +void esp_dma_enable(ESPState *s, int irq, int level); +void esp_request_cancelled(SCSIRequest *req); +void esp_command_complete(SCSIRequest *req, uint32_t status, size_t resid); +void esp_transfer_data(SCSIRequest *req, uint32_t len); +void esp_hard_reset(ESPState *s); +uint64_t esp_reg_read(ESPState *s, uint32_t saddr); +void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val); +extern const VMStateDescription vmstate_esp; + #endif diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index efea93f0b4..5ea3cadb01 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -636,7 +636,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) } } -static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) +static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset) { AHCICmdHdr *cmd = ad->cur_cmd; uint32_t opts = le32_to_cpu(cmd->opts); @@ -647,6 +647,10 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) uint8_t *prdt; int i; int r = 0; + int sum = 0; + int off_idx = -1; + int off_pos = -1; + int tbl_entry_size; if (!sglist_alloc_hint) { DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts); @@ -669,10 +673,31 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) /* Get entries in the PRDT, init a qemu sglist accordingly */ if (sglist_alloc_hint > 0) { AHCI_SG *tbl = (AHCI_SG *)prdt; - - qemu_sglist_init(sglist, sglist_alloc_hint, ad->hba->dma); + sum = 0; for (i = 0; i < sglist_alloc_hint; i++) { /* flags_size is zero-based */ + tbl_entry_size = (le32_to_cpu(tbl[i].flags_size) + 1); + if (offset <= (sum + tbl_entry_size)) { + off_idx = i; + off_pos = offset - sum; + break; + } + sum += tbl_entry_size; + } + if ((off_idx == -1) || (off_pos < 0) || (off_pos > tbl_entry_size)) { + DPRINTF(ad->port_no, "%s: Incorrect offset! " + "off_idx: %d, off_pos: %d\n", + __func__, off_idx, off_pos); + r = -1; + goto out; + } + + qemu_sglist_init(sglist, (sglist_alloc_hint - off_idx), ad->hba->dma); + qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr + off_pos), + le32_to_cpu(tbl[off_idx].flags_size) + 1 - off_pos); + + for (i = off_idx + 1; i < sglist_alloc_hint; i++) { + /* flags_size is zero-based */ qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr), le32_to_cpu(tbl[i].flags_size) + 1); } @@ -745,7 +770,7 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2, s->dev[port].port.ifs[0].nb_sectors - 1); - ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist); + ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist, 0); ncq_tfs->tag = tag; switch(ncq_fis->command) { @@ -970,7 +995,7 @@ static int ahci_start_transfer(IDEDMA *dma) goto out; } - if (!ahci_populate_sglist(ad, &s->sg)) { + if (!ahci_populate_sglist(ad, &s->sg, 0)) { has_sglist = 1; } @@ -1015,6 +1040,7 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s, DPRINTF(ad->port_no, "\n"); ad->dma_cb = dma_cb; ad->dma_status |= BM_STATUS_DMAING; + s->io_buffer_offset = 0; dma_cb(s, 0); } @@ -1023,7 +1049,7 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; - ahci_populate_sglist(ad, &s->sg); + ahci_populate_sglist(ad, &s->sg, 0); s->io_buffer_size = s->sg.size; DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size); @@ -1037,7 +1063,7 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) uint8_t *p = s->io_buffer + s->io_buffer_index; int l = s->io_buffer_size - s->io_buffer_index; - if (ahci_populate_sglist(ad, &s->sg)) { + if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset)) { return 0; } @@ -1047,9 +1073,13 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) dma_buf_write(p, l, &s->sg); } + /* free sglist that was created in ahci_populate_sglist() */ + qemu_sglist_destroy(&s->sg); + /* update number of transferred bytes */ ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l); s->io_buffer_index += l; + s->io_buffer_offset += l; DPRINTF(ad->port_no, "len=%#x\n", l); diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 7170bd9cd0..bf7d313cf4 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -393,6 +393,7 @@ struct IDEState { struct iovec iov; QEMUIOVector qiov; /* ATA DMA state */ + int io_buffer_offset; int io_buffer_size; QEMUSGList sg; /* PIO transfer handling */ diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 22e58dfc8a..5ea9b8f4b2 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -149,7 +149,8 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) } blkconf_serial(&dev->conf, &dev->serial); - if (blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255) < 0) { + if (kind != IDE_CD + && blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255) < 0) { return -1; } diff --git a/hw/kvm/i8259.c b/hw/kvm/i8259.c index 94d1b9aa95..1e24cd4f36 100644 --- a/hw/kvm/i8259.c +++ b/hw/kvm/i8259.c @@ -94,7 +94,7 @@ static void kvm_pic_set_irq(void *opaque, int irq, int level) { int delivered; - delivered = kvm_irqchip_set_irq(kvm_state, irq, level); + delivered = kvm_set_irq(kvm_state, irq, level); apic_report_irq_delivered(delivered); } diff --git a/hw/kvm/ioapic.c b/hw/kvm/ioapic.c index 3ae3175403..6c3b8fe39a 100644 --- a/hw/kvm/ioapic.c +++ b/hw/kvm/ioapic.c @@ -82,7 +82,7 @@ static void kvm_ioapic_set_irq(void *opaque, int irq, int level) KVMIOAPICState *s = opaque; int delivered; - delivered = kvm_irqchip_set_irq(kvm_state, s->kvm_gsi_base + irq, level); + delivered = kvm_set_irq(kvm_state, s->kvm_gsi_base + irq, level); apic_report_irq_delivered(delivered); } diff --git a/hw/pc.c b/hw/pc.c index 81c391cd6a..e8bcfc0b4b 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -42,6 +42,7 @@ #include "sysbus.h" #include "sysemu.h" #include "kvm.h" +#include "kvm_i386.h" #include "xen.h" #include "blockdev.h" #include "hw/block-common.h" diff --git a/hw/pci-stub.c b/hw/pci-stub.c index e083191529..134c4484b6 100644 --- a/hw/pci-stub.c +++ b/hw/pci-stub.c @@ -34,21 +34,6 @@ static void pci_error_message(Monitor *mon) monitor_printf(mon, "PCI devices not supported\n"); } -void pci_register_bar(PCIDevice *pci_dev, int region_num, - uint8_t type, MemoryRegion *memory) -{ -} - -const VMStateDescription vmstate_pci_device = { - .name = "PCIDeviceStub", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_END_OF_LIST() - } -}; - int do_pcie_aer_inject_error(Monitor *mon, const QDict *qdict, QObject **ret_data) { diff --git a/hw/petalogix_ml605_mmu.c b/hw/petalogix_ml605_mmu.c index 6a7d0c0bff..dced648f45 100644 --- a/hw/petalogix_ml605_mmu.c +++ b/hw/petalogix_ml605_mmu.c @@ -39,7 +39,8 @@ #include "microblaze_boot.h" #include "microblaze_pic_cpu.h" -#include "xilinx_axidma.h" + +#include "stream.h" #define LMB_BRAM_SIZE (128 * 1024) #define FLASH_SIZE (32 * 1024 * 1024) @@ -76,7 +77,7 @@ petalogix_ml605_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { MemoryRegion *address_space_mem = get_system_memory(); - DeviceState *dev; + DeviceState *dev, *dma, *eth0; MicroBlazeCPU *cpu; CPUMBState *env; DriveInfo *dinfo; @@ -125,15 +126,18 @@ petalogix_ml605_init(ram_addr_t ram_size, /* 2 timers at irq 2 @ 100 Mhz. */ xilinx_timer_create(TIMER_BASEADDR, irq[2], 0, 100 * 1000000); - /* axi ethernet and dma initialization. TODO: Dynamically connect them. */ - { - static struct XilinxDMAConnection dmach; + /* axi ethernet and dma initialization. */ + dma = qdev_create(NULL, "xlnx.axi-dma"); - xilinx_axiethernet_create(&dmach, &nd_table[0], 0x82780000, - irq[3], 0x1000, 0x1000); - xilinx_axiethernetdma_create(&dmach, 0x84600000, - irq[1], irq[0], 100 * 1000000); - } + /* FIXME: attach to the sysbus instead */ + object_property_add_child(container_get(qdev_get_machine(), "/unattached"), + "xilinx-dma", OBJECT(dma), NULL); + + eth0 = xilinx_axiethernet_create(&nd_table[0], STREAM_SLAVE(dma), + 0x82780000, irq[3], 0x1000, 0x1000); + + xilinx_axiethernetdma_init(dma, STREAM_SLAVE(eth0), + 0x84600000, irq[1], irq[0], 100 * 1000000); microblaze_load_kernel(cpu, ddr_base, ram_size, BINARY_DEVICE_TREE_FILE, machine_cpu_reset); diff --git a/hw/puv3.c b/hw/puv3.c new file mode 100644 index 0000000000..43f7216e4e --- /dev/null +++ b/hw/puv3.c @@ -0,0 +1,131 @@ +/* + * Generic PKUnity SoC machine and board descriptor + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "console.h" +#include "elf.h" +#include "exec-memory.h" +#include "sysbus.h" +#include "boards.h" +#include "loader.h" +#include "pc.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +#define KERNEL_LOAD_ADDR 0x03000000 +#define KERNEL_MAX_SIZE 0x00800000 /* Just a guess */ + +static void puv3_intc_cpu_handler(void *opaque, int irq, int level) +{ + CPUUniCore32State *env = opaque; + + assert(irq == 0); + if (level) { + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +} + +static void puv3_soc_init(CPUUniCore32State *env) +{ + qemu_irq *cpu_intc, irqs[PUV3_IRQS_NR]; + DeviceState *dev; + MemoryRegion *i8042 = g_new(MemoryRegion, 1); + int i; + + /* Initialize interrupt controller */ + cpu_intc = qemu_allocate_irqs(puv3_intc_cpu_handler, env, 1); + dev = sysbus_create_simple("puv3_intc", PUV3_INTC_BASE, *cpu_intc); + for (i = 0; i < PUV3_IRQS_NR; i++) { + irqs[i] = qdev_get_gpio_in(dev, i); + } + + /* Initialize minimal necessary devices for kernel booting */ + sysbus_create_simple("puv3_pm", PUV3_PM_BASE, NULL); + sysbus_create_simple("puv3_dma", PUV3_DMA_BASE, NULL); + sysbus_create_simple("puv3_ost", PUV3_OST_BASE, irqs[PUV3_IRQS_OST0]); + sysbus_create_varargs("puv3_gpio", PUV3_GPIO_BASE, + irqs[PUV3_IRQS_GPIOLOW0], irqs[PUV3_IRQS_GPIOLOW1], + irqs[PUV3_IRQS_GPIOLOW2], irqs[PUV3_IRQS_GPIOLOW3], + irqs[PUV3_IRQS_GPIOLOW4], irqs[PUV3_IRQS_GPIOLOW5], + irqs[PUV3_IRQS_GPIOLOW6], irqs[PUV3_IRQS_GPIOLOW7], + irqs[PUV3_IRQS_GPIOHIGH], NULL); + + /* Keyboard (i8042), mouse disabled for nographic */ + i8042_mm_init(irqs[PUV3_IRQS_PS2_KBD], NULL, i8042, PUV3_REGS_OFFSET, 4); + memory_region_add_subregion(get_system_memory(), PUV3_PS2_BASE, i8042); +} + +static void puv3_board_init(CPUUniCore32State *env, ram_addr_t ram_size) +{ + MemoryRegion *ram_memory = g_new(MemoryRegion, 1); + + /* SDRAM at address zero. */ + memory_region_init_ram(ram_memory, "puv3.ram", ram_size); + vmstate_register_ram_global(ram_memory); + memory_region_add_subregion(get_system_memory(), 0, ram_memory); +} + +static void puv3_load_kernel(const char *kernel_filename) +{ + int size; + + assert(kernel_filename != NULL); + + /* only zImage format supported */ + size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, + KERNEL_MAX_SIZE); + if (size < 0) { + hw_error("Load kernel error: '%s'\n", kernel_filename); + } + + /* cheat curses that we have a graphic console, only under ocd console */ + graphic_console_init(NULL, NULL, NULL, NULL, NULL); +} + +static void puv3_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) +{ + CPUUniCore32State *env; + + if (initrd_filename) { + hw_error("Please use kernel built-in initramdisk.\n"); + } + + if (!cpu_model) { + cpu_model = "UniCore-II"; + } + + env = cpu_init(cpu_model); + if (!env) { + hw_error("Unable to find CPU definition\n"); + } + + puv3_soc_init(env); + puv3_board_init(env, ram_size); + puv3_load_kernel(kernel_filename); +} + +static QEMUMachine puv3_machine = { + .name = "puv3", + .desc = "PKUnity Version-3 based on UniCore32", + .init = puv3_init, + .is_default = 1, + .use_scsi = 0, +}; + +static void puv3_machine_init(void) +{ + qemu_register_machine(&puv3_machine); +} + +machine_init(puv3_machine_init) diff --git a/hw/puv3.h b/hw/puv3.h new file mode 100644 index 0000000000..f37adcb665 --- /dev/null +++ b/hw/puv3.h @@ -0,0 +1,49 @@ +/* + * Misc PKUnity SoC declarations + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_HW_PUV3_H +#define QEMU_HW_PUV3_H + +#define PUV3_REGS_OFFSET (0x1000) /* 4K is reasonable */ + +/* PKUnity System bus (AHB): 0xc0000000 - 0xedffffff (640MB) */ +#define PUV3_DMA_BASE (0xc0200000) /* AHB-4 */ + +/* PKUnity Peripheral bus (APB): 0xee000000 - 0xefffffff (128MB) */ +#define PUV3_GPIO_BASE (0xee500000) /* APB-5 */ +#define PUV3_INTC_BASE (0xee600000) /* APB-6 */ +#define PUV3_OST_BASE (0xee800000) /* APB-8 */ +#define PUV3_PM_BASE (0xeea00000) /* APB-10 */ +#define PUV3_PS2_BASE (0xeeb00000) /* APB-11 */ + +/* Hardware interrupts */ +#define PUV3_IRQS_NR (32) + +#define PUV3_IRQS_GPIOLOW0 (0) +#define PUV3_IRQS_GPIOLOW1 (1) +#define PUV3_IRQS_GPIOLOW2 (2) +#define PUV3_IRQS_GPIOLOW3 (3) +#define PUV3_IRQS_GPIOLOW4 (4) +#define PUV3_IRQS_GPIOLOW5 (5) +#define PUV3_IRQS_GPIOLOW6 (6) +#define PUV3_IRQS_GPIOLOW7 (7) +#define PUV3_IRQS_GPIOHIGH (8) +#define PUV3_IRQS_PS2_KBD (22) +#define PUV3_IRQS_PS2_AUX (23) +#define PUV3_IRQS_OST0 (26) + +/* All puv3_*.c use DPRINTF for debug. */ +#ifdef DEBUG_PUV3 +#define DPRINTF(fmt, ...) printf("%s: " fmt , __func__, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#endif /* !QEMU_HW_PUV3_H */ diff --git a/hw/puv3_dma.c b/hw/puv3_dma.c new file mode 100644 index 0000000000..85b97bfdeb --- /dev/null +++ b/hw/puv3_dma.c @@ -0,0 +1,109 @@ +/* + * DMA device simulation in PKUnity SoC + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "hw.h" +#include "sysbus.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +#define PUV3_DMA_CH_NR (6) +#define PUV3_DMA_CH_MASK (0xff) +#define PUV3_DMA_CH(offset) ((offset) >> 8) + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + uint32_t reg_CFG[PUV3_DMA_CH_NR]; +} PUV3DMAState; + +static uint64_t puv3_dma_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + PUV3DMAState *s = opaque; + uint32_t ret = 0; + + assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR); + + switch (offset & PUV3_DMA_CH_MASK) { + case 0x10: + ret = s->reg_CFG[PUV3_DMA_CH(offset)]; + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); + + return ret; +} + +static void puv3_dma_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + PUV3DMAState *s = opaque; + + assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR); + + switch (offset & PUV3_DMA_CH_MASK) { + case 0x10: + s->reg_CFG[PUV3_DMA_CH(offset)] = value; + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, value); +} + +static const MemoryRegionOps puv3_dma_ops = { + .read = puv3_dma_read, + .write = puv3_dma_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int puv3_dma_init(SysBusDevice *dev) +{ + PUV3DMAState *s = FROM_SYSBUS(PUV3DMAState, dev); + int i; + + for (i = 0; i < PUV3_DMA_CH_NR; i++) { + s->reg_CFG[i] = 0x0; + } + + memory_region_init_io(&s->iomem, &puv3_dma_ops, s, "puv3_dma", + PUV3_REGS_OFFSET); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void puv3_dma_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = puv3_dma_init; +} + +static const TypeInfo puv3_dma_info = { + .name = "puv3_dma", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PUV3DMAState), + .class_init = puv3_dma_class_init, +}; + +static void puv3_dma_register_type(void) +{ + type_register_static(&puv3_dma_info); +} + +type_init(puv3_dma_register_type) diff --git a/hw/puv3_gpio.c b/hw/puv3_gpio.c new file mode 100644 index 0000000000..9436e6c62c --- /dev/null +++ b/hw/puv3_gpio.c @@ -0,0 +1,141 @@ +/* + * GPIO device simulation in PKUnity SoC + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "hw.h" +#include "sysbus.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq[9]; + + uint32_t reg_GPLR; + uint32_t reg_GPDR; + uint32_t reg_GPIR; +} PUV3GPIOState; + +static uint64_t puv3_gpio_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + PUV3GPIOState *s = opaque; + uint32_t ret = 0; + + switch (offset) { + case 0x00: + ret = s->reg_GPLR; + break; + case 0x04: + ret = s->reg_GPDR; + break; + case 0x20: + ret = s->reg_GPIR; + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); + + return ret; +} + +static void puv3_gpio_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + PUV3GPIOState *s = opaque; + + DPRINTF("offset 0x%x, value 0x%x\n", offset, value); + switch (offset) { + case 0x04: + s->reg_GPDR = value; + break; + case 0x08: + if (s->reg_GPDR & value) { + s->reg_GPLR |= value; + } else { + DPRINTF("Write gpio input port error!"); + } + break; + case 0x0c: + if (s->reg_GPDR & value) { + s->reg_GPLR &= ~value; + } else { + DPRINTF("Write gpio input port error!"); + } + break; + case 0x10: /* GRER */ + case 0x14: /* GFER */ + case 0x18: /* GEDR */ + break; + case 0x20: /* GPIR */ + s->reg_GPIR = value; + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } +} + +static const MemoryRegionOps puv3_gpio_ops = { + .read = puv3_gpio_read, + .write = puv3_gpio_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int puv3_gpio_init(SysBusDevice *dev) +{ + PUV3GPIOState *s = FROM_SYSBUS(PUV3GPIOState, dev); + + s->reg_GPLR = 0; + s->reg_GPDR = 0; + + /* FIXME: these irqs not handled yet */ + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW0]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW1]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW2]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW3]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW4]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW5]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW6]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW7]); + sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOHIGH]); + + memory_region_init_io(&s->iomem, &puv3_gpio_ops, s, "puv3_gpio", + PUV3_REGS_OFFSET); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void puv3_gpio_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = puv3_gpio_init; +} + +static const TypeInfo puv3_gpio_info = { + .name = "puv3_gpio", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PUV3GPIOState), + .class_init = puv3_gpio_class_init, +}; + +static void puv3_gpio_register_type(void) +{ + type_register_static(&puv3_gpio_info); +} + +type_init(puv3_gpio_register_type) diff --git a/hw/puv3_intc.c b/hw/puv3_intc.c new file mode 100644 index 0000000000..9e0b975ea2 --- /dev/null +++ b/hw/puv3_intc.c @@ -0,0 +1,135 @@ +/* + * INTC device simulation in PKUnity SoC + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "sysbus.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq parent_irq; + + uint32_t reg_ICMR; + uint32_t reg_ICPR; +} PUV3INTCState; + +/* Update interrupt status after enabled or pending bits have been changed. */ +static void puv3_intc_update(PUV3INTCState *s) +{ + if (s->reg_ICMR & s->reg_ICPR) { + qemu_irq_raise(s->parent_irq); + } else { + qemu_irq_lower(s->parent_irq); + } +} + +/* Process a change in an external INTC input. */ +static void puv3_intc_handler(void *opaque, int irq, int level) +{ + PUV3INTCState *s = opaque; + + DPRINTF("irq 0x%x, level 0x%x\n", irq, level); + if (level) { + s->reg_ICPR |= (1 << irq); + } else { + s->reg_ICPR &= ~(1 << irq); + } + puv3_intc_update(s); +} + +static uint64_t puv3_intc_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + PUV3INTCState *s = opaque; + uint32_t ret = 0; + + switch (offset) { + case 0x04: /* INTC_ICMR */ + ret = s->reg_ICMR; + break; + case 0x0c: /* INTC_ICIP */ + ret = s->reg_ICPR; /* the same value with ICPR */ + break; + default: + DPRINTF("Bad offset %x\n", (int)offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); + return ret; +} + +static void puv3_intc_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + PUV3INTCState *s = opaque; + + DPRINTF("offset 0x%x, value 0x%x\n", offset, value); + switch (offset) { + case 0x00: /* INTC_ICLR */ + case 0x14: /* INTC_ICCR */ + break; + case 0x04: /* INTC_ICMR */ + s->reg_ICMR = value; + break; + default: + DPRINTF("Bad offset 0x%x\n", (int)offset); + return; + } + puv3_intc_update(s); +} + +static const MemoryRegionOps puv3_intc_ops = { + .read = puv3_intc_read, + .write = puv3_intc_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int puv3_intc_init(SysBusDevice *dev) +{ + PUV3INTCState *s = FROM_SYSBUS(PUV3INTCState, dev); + + qdev_init_gpio_in(&s->busdev.qdev, puv3_intc_handler, PUV3_IRQS_NR); + sysbus_init_irq(&s->busdev, &s->parent_irq); + + s->reg_ICMR = 0; + s->reg_ICPR = 0; + + memory_region_init_io(&s->iomem, &puv3_intc_ops, s, "puv3_intc", + PUV3_REGS_OFFSET); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void puv3_intc_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = puv3_intc_init; +} + +static const TypeInfo puv3_intc_info = { + .name = "puv3_intc", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PUV3INTCState), + .class_init = puv3_intc_class_init, +}; + +static void puv3_intc_register_type(void) +{ + type_register_static(&puv3_intc_info); +} + +type_init(puv3_intc_register_type) diff --git a/hw/puv3_ost.c b/hw/puv3_ost.c new file mode 100644 index 0000000000..dd30cad0e2 --- /dev/null +++ b/hw/puv3_ost.c @@ -0,0 +1,151 @@ +/* + * OSTimer device simulation in PKUnity SoC + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "sysbus.h" +#include "ptimer.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +/* puv3 ostimer implementation. */ +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + QEMUBH *bh; + qemu_irq irq; + ptimer_state *ptimer; + + uint32_t reg_OSMR0; + uint32_t reg_OSCR; + uint32_t reg_OSSR; + uint32_t reg_OIER; +} PUV3OSTState; + +static uint64_t puv3_ost_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + PUV3OSTState *s = opaque; + uint32_t ret = 0; + + switch (offset) { + case 0x10: /* Counter Register */ + ret = s->reg_OSMR0 - (uint32_t)ptimer_get_count(s->ptimer); + break; + case 0x14: /* Status Register */ + ret = s->reg_OSSR; + break; + case 0x1c: /* Interrupt Enable Register */ + ret = s->reg_OIER; + break; + default: + DPRINTF("Bad offset %x\n", (int)offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); + return ret; +} + +static void puv3_ost_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + PUV3OSTState *s = opaque; + + DPRINTF("offset 0x%x, value 0x%x\n", offset, value); + switch (offset) { + case 0x00: /* Match Register 0 */ + s->reg_OSMR0 = value; + if (s->reg_OSMR0 > s->reg_OSCR) { + ptimer_set_count(s->ptimer, s->reg_OSMR0 - s->reg_OSCR); + } else { + ptimer_set_count(s->ptimer, s->reg_OSMR0 + + (0xffffffff - s->reg_OSCR)); + } + ptimer_run(s->ptimer, 2); + break; + case 0x14: /* Status Register */ + assert(value == 0); + if (s->reg_OSSR) { + s->reg_OSSR = value; + qemu_irq_lower(s->irq); + } + break; + case 0x1c: /* Interrupt Enable Register */ + s->reg_OIER = value; + break; + default: + DPRINTF("Bad offset %x\n", (int)offset); + } +} + +static const MemoryRegionOps puv3_ost_ops = { + .read = puv3_ost_read, + .write = puv3_ost_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void puv3_ost_tick(void *opaque) +{ + PUV3OSTState *s = opaque; + + DPRINTF("ost hit when ptimer counter from 0x%x to 0x%x!\n", + s->reg_OSCR, s->reg_OSMR0); + + s->reg_OSCR = s->reg_OSMR0; + if (s->reg_OIER) { + s->reg_OSSR = 1; + qemu_irq_raise(s->irq); + } +} + +static int puv3_ost_init(SysBusDevice *dev) +{ + PUV3OSTState *s = FROM_SYSBUS(PUV3OSTState, dev); + + s->reg_OIER = 0; + s->reg_OSSR = 0; + s->reg_OSMR0 = 0; + s->reg_OSCR = 0; + + sysbus_init_irq(dev, &s->irq); + + s->bh = qemu_bh_new(puv3_ost_tick, s); + s->ptimer = ptimer_init(s->bh); + ptimer_set_freq(s->ptimer, 50 * 1000 * 1000); + + memory_region_init_io(&s->iomem, &puv3_ost_ops, s, "puv3_ost", + PUV3_REGS_OFFSET); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void puv3_ost_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = puv3_ost_init; +} + +static const TypeInfo puv3_ost_info = { + .name = "puv3_ost", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PUV3OSTState), + .class_init = puv3_ost_class_init, +}; + +static void puv3_ost_register_type(void) +{ + type_register_static(&puv3_ost_info); +} + +type_init(puv3_ost_register_type) diff --git a/hw/puv3_pm.c b/hw/puv3_pm.c new file mode 100644 index 0000000000..621c96875c --- /dev/null +++ b/hw/puv3_pm.c @@ -0,0 +1,149 @@ +/* + * Power Management device simulation in PKUnity SoC + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "hw.h" +#include "sysbus.h" + +#undef DEBUG_PUV3 +#include "puv3.h" + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + + uint32_t reg_PMCR; + uint32_t reg_PCGR; + uint32_t reg_PLL_SYS_CFG; + uint32_t reg_PLL_DDR_CFG; + uint32_t reg_PLL_VGA_CFG; + uint32_t reg_DIVCFG; +} PUV3PMState; + +static uint64_t puv3_pm_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + PUV3PMState *s = opaque; + uint32_t ret = 0; + + switch (offset) { + case 0x14: + ret = s->reg_PCGR; + break; + case 0x18: + ret = s->reg_PLL_SYS_CFG; + break; + case 0x1c: + ret = s->reg_PLL_DDR_CFG; + break; + case 0x20: + ret = s->reg_PLL_VGA_CFG; + break; + case 0x24: + ret = s->reg_DIVCFG; + break; + case 0x28: /* PLL SYS STATUS */ + ret = 0x00002401; + break; + case 0x2c: /* PLL DDR STATUS */ + ret = 0x00100c00; + break; + case 0x30: /* PLL VGA STATUS */ + ret = 0x00003801; + break; + case 0x34: /* DIV STATUS */ + ret = 0x22f52015; + break; + case 0x38: /* SW RESET */ + ret = 0x0; + break; + case 0x44: /* PLL DFC DONE */ + ret = 0x7; + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, ret); + + return ret; +} + +static void puv3_pm_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + PUV3PMState *s = opaque; + + switch (offset) { + case 0x0: + s->reg_PMCR = value; + break; + case 0x14: + s->reg_PCGR = value; + break; + case 0x18: + s->reg_PLL_SYS_CFG = value; + break; + case 0x1c: + s->reg_PLL_DDR_CFG = value; + break; + case 0x20: + s->reg_PLL_VGA_CFG = value; + break; + case 0x24: + case 0x38: + break; + default: + DPRINTF("Bad offset 0x%x\n", offset); + } + DPRINTF("offset 0x%x, value 0x%x\n", offset, value); +} + +static const MemoryRegionOps puv3_pm_ops = { + .read = puv3_pm_read, + .write = puv3_pm_write, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int puv3_pm_init(SysBusDevice *dev) +{ + PUV3PMState *s = FROM_SYSBUS(PUV3PMState, dev); + + s->reg_PCGR = 0x0; + + memory_region_init_io(&s->iomem, &puv3_pm_ops, s, "puv3_pm", + PUV3_REGS_OFFSET); + sysbus_init_mmio(dev, &s->iomem); + + return 0; +} + +static void puv3_pm_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + + sdc->init = puv3_pm_init; +} + +static const TypeInfo puv3_pm_info = { + .name = "puv3_pm", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PUV3PMState), + .class_init = puv3_pm_class_init, +}; + +static void puv3_pm_register_type(void) +{ + type_register_static(&puv3_pm_info); +} + +type_init(puv3_pm_register_type) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 6120cc83c1..b8a857d145 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -1437,7 +1437,6 @@ static const char *scsi_command_name(uint8_t cmd) [ ATA_PASSTHROUGH_12 ] = "BLANK/ATA_PASSTHROUGH_12", [ MOVE_MEDIUM ] = "MOVE_MEDIUM", [ EXCHANGE_MEDIUM ] = "EXCHANGE MEDIUM", - [ LOAD_UNLOAD ] = "LOAD_UNLOAD", [ READ_12 ] = "READ_12", [ WRITE_12 ] = "WRITE_12", [ ERASE_12 ] = "ERASE_12/GET_PERFORMANCE", diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index a9c727905a..409f760ef7 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -175,6 +175,8 @@ static void scsi_aio_complete(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; bdrv_acct_done(s->qdev.conf.bs, &r->acct); if (ret < 0) { @@ -238,10 +240,9 @@ static void scsi_dma_complete(void *opaque, int ret) SCSIDiskReq *r = (SCSIDiskReq *)opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - if (r->req.aiocb != NULL) { - r->req.aiocb = NULL; - bdrv_acct_done(s->qdev.conf.bs, &r->acct); - } + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; + bdrv_acct_done(s->qdev.conf.bs, &r->acct); if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { @@ -270,10 +271,9 @@ static void scsi_read_complete(void * opaque, int ret) SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); int n; - if (r->req.aiocb != NULL) { - r->req.aiocb = NULL; - bdrv_acct_done(s->qdev.conf.bs, &r->acct); - } + assert(r->req.aiocb != NULL); + r->req.aiocb = NULL; + bdrv_acct_done(s->qdev.conf.bs, &r->acct); if (ret < 0) { if (scsi_handle_rw_error(r, -ret)) { @@ -637,7 +637,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { buflen = 8; outbuf[4] = 0; - outbuf[5] = 0x60; /* write_same 10/16 supported */ + outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */ outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; outbuf[7] = 0; break; @@ -1449,6 +1449,89 @@ invalid_field: return; } +typedef struct UnmapCBData { + SCSIDiskReq *r; + uint8_t *inbuf; + int count; +} UnmapCBData; + +static void scsi_unmap_complete(void *opaque, int ret) +{ + UnmapCBData *data = opaque; + SCSIDiskReq *r = data->r; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + uint64_t sector_num; + uint32 nb_sectors; + + r->req.aiocb = NULL; + if (ret < 0) { + if (scsi_handle_rw_error(r, -ret)) { + goto done; + } + } + + if (data->count > 0 && !r->req.io_canceled) { + sector_num = ldq_be_p(&data->inbuf[0]); + nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL; + if (sector_num > sector_num + nb_sectors || + sector_num + nb_sectors - 1 > s->qdev.max_lba) { + scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); + goto done; + } + + r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs, + sector_num * (s->qdev.blocksize / 512), + nb_sectors * (s->qdev.blocksize / 512), + scsi_unmap_complete, data); + data->count--; + data->inbuf += 16; + return; + } + +done: + if (data->count == 0) { + scsi_req_complete(&r->req, GOOD); + } + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } + g_free(data); +} + +static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf) +{ + uint8_t *p = inbuf; + int len = r->req.cmd.xfer; + UnmapCBData *data; + + if (len < 8) { + goto invalid_param_len; + } + if (len < lduw_be_p(&p[0]) + 2) { + goto invalid_param_len; + } + if (len < lduw_be_p(&p[2]) + 8) { + goto invalid_param_len; + } + if (lduw_be_p(&p[2]) & 15) { + goto invalid_param_len; + } + + data = g_new0(UnmapCBData, 1); + data->r = r; + data->inbuf = &p[8]; + data->count = lduw_be_p(&p[2]) >> 4; + + /* The matching unref is in scsi_unmap_complete, before data is freed. */ + scsi_req_ref(&r->req); + scsi_unmap_complete(data, 0); + return; + +invalid_param_len: + scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); + return; +} + static void scsi_disk_emulate_write_data(SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); @@ -1468,6 +1551,10 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req) scsi_disk_emulate_mode_select(r, r->iov.iov_base); break; + case UNMAP: + scsi_disk_emulate_unmap(r, r->iov.iov_base); + break; + default: abort(); } @@ -1702,6 +1789,9 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) case MODE_SELECT_10: DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer); break; + case UNMAP: + DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer); + break; case WRITE_SAME_10: nb_sectors = lduw_be_p(&req->cmd.buf[7]); goto write_same; @@ -1712,7 +1802,8 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); return 0; } - if (r->req.cmd.lba > s->qdev.max_lba) { + if (r->req.cmd.lba > r->req.cmd.lba + nb_sectors || + r->req.cmd.lba + nb_sectors - 1 > s->qdev.max_lba) { goto illegal_lba; } @@ -1958,7 +2049,8 @@ static int scsi_initfn(SCSIDevice *dev) } blkconf_serial(&s->qdev.conf, &s->serial); - if (blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) { + if (dev->type == TYPE_DISK + && blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) { return -1; } @@ -2066,6 +2158,7 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { [SEEK_10] = &scsi_disk_emulate_reqops, [MODE_SELECT] = &scsi_disk_emulate_reqops, [MODE_SELECT_10] = &scsi_disk_emulate_reqops, + [UNMAP] = &scsi_disk_emulate_reqops, [WRITE_SAME_10] = &scsi_disk_emulate_reqops, [WRITE_SAME_16] = &scsi_disk_emulate_reqops, diff --git a/hw/sd.c b/hw/sd.c index 07eb263388..ec26407543 100644 --- a/hw/sd.c +++ b/hw/sd.c @@ -32,6 +32,7 @@ #include "hw.h" #include "block.h" #include "sd.h" +#include "bitmap.h" //#define DEBUG_SD 1 @@ -80,8 +81,8 @@ struct SDState { uint32_t card_status; uint8_t sd_status[64]; uint32_t vhs; - int wp_switch; - int *wp_groups; + bool wp_switch; + unsigned long *wp_groups; uint64_t size; int blk_len; uint32_t erase_start; @@ -90,12 +91,12 @@ struct SDState { int pwd_len; int function_group[6]; - int spi; + bool spi; int current_cmd; /* True if we will handle the next command as an ACMD. Note that this does * *not* track the APP_CMD status bit! */ - int expecting_acmd; + bool expecting_acmd; int blk_written; uint64_t data_start; uint32_t data_offset; @@ -105,7 +106,7 @@ struct SDState { BlockDriverState *bdrv; uint8_t *buf; - int enable; + bool enable; }; static void sd_set_mode(SDState *sd) @@ -387,6 +388,11 @@ static void sd_response_r7_make(SDState *sd, uint8_t *response) response[3] = (sd->vhs >> 0) & 0xff; } +static inline uint64_t sd_addr_to_wpnum(uint64_t addr) +{ + return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); +} + static void sd_reset(SDState *sd, BlockDriverState *bdrv) { uint64_t size; @@ -399,7 +405,7 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv) } size = sect << 9; - sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1; + sect = sd_addr_to_wpnum(size) + 1; sd->state = sd_idle_state; sd->rca = 0x0000; @@ -414,15 +420,15 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv) if (sd->wp_groups) g_free(sd->wp_groups); - sd->wp_switch = bdrv ? bdrv_is_read_only(bdrv) : 0; - sd->wp_groups = (int *) g_malloc0(sizeof(int) * sect); + sd->wp_switch = bdrv ? bdrv_is_read_only(bdrv) : false; + sd->wp_groups = bitmap_new(sect); memset(sd->function_group, 0, sizeof(int) * 6); sd->erase_start = 0; sd->erase_end = 0; sd->size = size; sd->blk_len = 0x200; sd->pwd_len = 0; - sd->expecting_acmd = 0; + sd->expecting_acmd = false; } static void sd_cardchange(void *opaque, bool load) @@ -444,14 +450,14 @@ static const BlockDevOps sd_block_ops = { whether card should be in SSI or MMC/SD mode. It is also up to the board to ensure that ssi transfers only occur when the chip select is asserted. */ -SDState *sd_init(BlockDriverState *bs, int is_spi) +SDState *sd_init(BlockDriverState *bs, bool is_spi) { SDState *sd; sd = (SDState *) g_malloc0(sizeof(SDState)); sd->buf = qemu_blockalign(bs, 512); sd->spi = is_spi; - sd->enable = 1; + sd->enable = true; sd_reset(sd, bs); if (sd->bdrv) { bdrv_attach_dev_nofail(sd->bdrv, sd); @@ -476,17 +482,17 @@ static void sd_erase(SDState *sd) return; } - start = sd->erase_start >> - (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); - end = sd->erase_end >> - (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); + start = sd_addr_to_wpnum(sd->erase_start); + end = sd_addr_to_wpnum(sd->erase_end); sd->erase_start = 0; sd->erase_end = 0; sd->csd[14] |= 0x40; - for (i = start; i <= end; i ++) - if (sd->wp_groups[i]) + for (i = start; i <= end; i++) { + if (test_bit(i, sd->wp_groups)) { sd->card_status |= WP_ERASE_SKIP; + } + } } static uint32_t sd_wpbits(SDState *sd, uint64_t addr) @@ -494,11 +500,13 @@ static uint32_t sd_wpbits(SDState *sd, uint64_t addr) uint32_t i, wpnum; uint32_t ret = 0; - wpnum = addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); + wpnum = sd_addr_to_wpnum(addr); - for (i = 0; i < 32; i ++, wpnum ++, addr += WPGROUP_SIZE) - if (addr < sd->size && sd->wp_groups[wpnum]) + for (i = 0; i < 32; i++, wpnum++, addr += WPGROUP_SIZE) { + if (addr < sd->size && test_bit(wpnum, sd->wp_groups)) { ret |= (1 << i); + } + } return ret; } @@ -534,10 +542,9 @@ static void sd_function_switch(SDState *sd, uint32_t arg) sd->data[66] = crc & 0xff; } -static inline int sd_wp_addr(SDState *sd, uint32_t addr) +static inline bool sd_wp_addr(SDState *sd, uint64_t addr) { - return sd->wp_groups[addr >> - (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)]; + return test_bit(sd_addr_to_wpnum(addr), sd->wp_groups); } static void sd_lock_command(SDState *sd) @@ -560,8 +567,7 @@ static void sd_lock_command(SDState *sd) sd->card_status |= LOCK_UNLOCK_FAILED; return; } - memset(sd->wp_groups, 0, sizeof(int) * (sd->size >> - (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT))); + bitmap_zero(sd->wp_groups, sd_addr_to_wpnum(sd->size) + 1); sd->csd[14] &= ~0x10; sd->card_status &= ~CARD_IS_LOCKED; sd->pwd_len = 0; @@ -1007,8 +1013,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } sd->state = sd_programming_state; - sd->wp_groups[addr >> (HWBLOCK_SHIFT + - SECTOR_SHIFT + WPGROUP_SHIFT)] = 1; + set_bit(sd_addr_to_wpnum(addr), sd->wp_groups); /* Bzzzzzzztt .... Operation complete. */ sd->state = sd_transfer_state; return sd_r1b; @@ -1027,8 +1032,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } sd->state = sd_programming_state; - sd->wp_groups[addr >> (HWBLOCK_SHIFT + - SECTOR_SHIFT + WPGROUP_SHIFT)] = 0; + clear_bit(sd_addr_to_wpnum(addr), sd->wp_groups); /* Bzzzzzzztt .... Operation complete. */ sd->state = sd_transfer_state; return sd_r1b; @@ -1125,7 +1129,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, if (sd->rca != rca) return sd_r0; - sd->expecting_acmd = 1; + sd->expecting_acmd = true; sd->card_status |= APP_CMD; return sd_r1; @@ -1307,7 +1311,7 @@ int sd_do_command(SDState *sd, SDRequest *req, if (sd->card_status & CARD_IS_LOCKED) { if (!cmd_valid_while_locked(sd, req)) { sd->card_status |= ILLEGAL_COMMAND; - sd->expecting_acmd = 0; + sd->expecting_acmd = false; fprintf(stderr, "SD: Card is locked\n"); rtype = sd_illegal; goto send_response; @@ -1318,7 +1322,7 @@ int sd_do_command(SDState *sd, SDRequest *req, sd_set_mode(sd); if (sd->expecting_acmd) { - sd->expecting_acmd = 0; + sd->expecting_acmd = false; rtype = sd_app_command(sd, *req); } else { rtype = sd_normal_command(sd, *req); @@ -1699,12 +1703,12 @@ uint8_t sd_read_data(SDState *sd) return ret; } -int sd_data_ready(SDState *sd) +bool sd_data_ready(SDState *sd) { return sd->state == sd_sendingdata_state; } -void sd_enable(SDState *sd, int enable) +void sd_enable(SDState *sd, bool enable) { sd->enable = enable; } diff --git a/hw/sd.h b/hw/sd.h index ac4b7c4dfa..4eb9679acd 100644 --- a/hw/sd.h +++ b/hw/sd.h @@ -67,13 +67,13 @@ typedef struct { typedef struct SDState SDState; -SDState *sd_init(BlockDriverState *bs, int is_spi); +SDState *sd_init(BlockDriverState *bs, bool is_spi); int sd_do_command(SDState *sd, SDRequest *req, uint8_t *response); void sd_write_data(SDState *sd, uint8_t value); uint8_t sd_read_data(SDState *sd); void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert); -int sd_data_ready(SDState *sd); -void sd_enable(SDState *sd, int enable); +bool sd_data_ready(SDState *sd); +void sd_enable(SDState *sd, bool enable); #endif /* __hw_sd_h */ diff --git a/hw/ssd0323.c b/hw/ssd0323.c index b0b2e94a81..b101c5112c 100644 --- a/hw/ssd0323.c +++ b/hw/ssd0323.c @@ -19,7 +19,9 @@ #define DPRINTF(fmt, ...) \ do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0) #define BADF(fmt, ...) \ -do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) +do { \ + fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); abort(); \ +} while (0) #else #define DPRINTF(fmt, ...) do {} while(0) #define BADF(fmt, ...) \ diff --git a/hw/stream.c b/hw/stream.c new file mode 100644 index 0000000000..be57e8b247 --- /dev/null +++ b/hw/stream.c @@ -0,0 +1,23 @@ +#include "stream.h" + +void +stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app) +{ + StreamSlaveClass *k = STREAM_SLAVE_GET_CLASS(sink); + + k->push(sink, buf, len, app); +} + +static TypeInfo stream_slave_info = { + .name = TYPE_STREAM_SLAVE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(StreamSlaveClass), +}; + + +static void stream_slave_register_types(void) +{ + type_register_static(&stream_slave_info); +} + +type_init(stream_slave_register_types) diff --git a/hw/stream.h b/hw/stream.h new file mode 100644 index 0000000000..21123a9089 --- /dev/null +++ b/hw/stream.h @@ -0,0 +1,31 @@ +#ifndef STREAM_H +#define STREAM_H 1 + +#include "qemu-common.h" +#include "qemu/object.h" + +/* stream slave. Used until qdev provides a generic way. */ +#define TYPE_STREAM_SLAVE "stream-slave" + +#define STREAM_SLAVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(StreamSlaveClass, (klass), TYPE_STREAM_SLAVE) +#define STREAM_SLAVE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(StreamSlaveClass, (obj), TYPE_STREAM_SLAVE) +#define STREAM_SLAVE(obj) \ + INTERFACE_CHECK(StreamSlave, (obj), TYPE_STREAM_SLAVE) + +typedef struct StreamSlave { + Object Parent; +} StreamSlave; + +typedef struct StreamSlaveClass { + InterfaceClass parent; + + void (*push)(StreamSlave *obj, unsigned char *buf, size_t len, + uint32_t *app); +} StreamSlaveClass; + +void +stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app); + +#endif /* STREAM_H */ diff --git a/hw/sun4m.c b/hw/sun4m.c index a959261209..0f909b5f86 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -832,6 +832,10 @@ static void cpu_devinit(const char *cpu_model, unsigned int id, env->prom_addr = prom_addr; } +static void dummy_fdc_tc(void *opaque, int irq, int level) +{ +} + static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, const char *boot_device, const char *kernel_filename, @@ -942,9 +946,6 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, serial_hds[0], serial_hds[1], ESCC_CLOCK, 1); cpu_halt = qemu_allocate_irqs(cpu_halt_signal, NULL, 1); - slavio_misc_init(hwdef->slavio_base, hwdef->aux1_base, hwdef->aux2_base, - slavio_irq[30], fdc_tc); - if (hwdef->apc_base) { apc_init(hwdef->apc_base, cpu_halt[0]); } @@ -955,8 +956,13 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, fd[0] = drive_get(IF_FLOPPY, 0, 0); sun4m_fdctrl_init(slavio_irq[22], hwdef->fd_base, fd, &fdc_tc); + } else { + fdc_tc = *qemu_allocate_irqs(dummy_fdc_tc, NULL, 1); } + slavio_misc_init(hwdef->slavio_base, hwdef->aux1_base, hwdef->aux2_base, + slavio_irq[30], fdc_tc); + if (drive_get_max_bus(IF_SCSI) > 0) { fprintf(stderr, "qemu: too many SCSI bus\n"); exit(1); @@ -1772,16 +1778,18 @@ static void sun4c_hw_init(const struct sun4c_hwdef *hwdef, ram_addr_t RAM_size, slavio_irq[1], serial_hds[0], serial_hds[1], ESCC_CLOCK, 1); - slavio_misc_init(0, hwdef->aux1_base, 0, slavio_irq[1], fdc_tc); - if (hwdef->fd_base != (target_phys_addr_t)-1) { /* there is zero or one floppy drive */ memset(fd, 0, sizeof(fd)); fd[0] = drive_get(IF_FLOPPY, 0, 0); sun4m_fdctrl_init(slavio_irq[1], hwdef->fd_base, fd, &fdc_tc); + } else { + fdc_tc = *qemu_allocate_irqs(dummy_fdc_tc, NULL, 1); } + slavio_misc_init(0, hwdef->aux1_base, 0, slavio_irq[1], fdc_tc); + if (drive_get_max_bus(IF_SCSI) > 0) { fprintf(stderr, "qemu: too many SCSI bus\n"); exit(1); diff --git a/hw/unicore32/Makefile.objs b/hw/unicore32/Makefile.objs new file mode 100644 index 0000000000..0725ce3ca7 --- /dev/null +++ b/hw/unicore32/Makefile.objs @@ -0,0 +1,6 @@ +# For UniCore32 machines and boards + +# PKUnity-v3 SoC and board information +obj-${CONFIG_PUV3} += puv3.o + +obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 7fa8b83d2e..ff48d91049 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -247,6 +247,9 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r the status read packet. */ usb_msd_send_status(s, p); s->mode = USB_MSDM_CBW; + } else if (s->mode == USB_MSDM_CSW) { + usb_msd_send_status(s, p); + s->mode = USB_MSDM_CBW; } else { if (s->data_len) { int len = (p->iov.size - p->result); @@ -383,6 +386,9 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) assert(le32_to_cpu(s->csw.residue) == 0); s->scsi_len = 0; s->req = scsi_req_new(s->scsi_dev, tag, 0, cbw.cmd, NULL); +#ifdef DEBUG_MSD + scsi_req_print(s->req); +#endif scsi_req_enqueue(s->req); if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) { scsi_req_continue(s->req); @@ -410,7 +416,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) } } if (p->result < p->iov.size) { - DPRINTF("Deferring packet %p\n", p); + DPRINTF("Deferring packet %p [wait data-out]\n", p); s->packet = p; ret = USB_RET_ASYNC; } else { @@ -445,6 +451,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) if (s->req) { /* still in flight */ + DPRINTF("Deferring packet %p [wait status]\n", p); s->packet = p; ret = USB_RET_ASYNC; } else { @@ -471,7 +478,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) } } if (p->result < p->iov.size) { - DPRINTF("Deferring packet %p\n", p); + DPRINTF("Deferring packet %p [wait data-in]\n", p); s->packet = p; ret = USB_RET_ASYNC; } else { diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index f21757ed55..fd8fa90792 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -254,6 +254,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) virtio_blk_req_complete(req, status); g_free(req); + return; #else abort(); #endif @@ -509,9 +510,19 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) blkcfg.size_max = 0; blkcfg.physical_block_exp = get_physical_block_exp(s->conf); blkcfg.alignment_offset = 0; + blkcfg.wce = bdrv_enable_write_cache(s->bs); memcpy(config, &blkcfg, sizeof(struct virtio_blk_config)); } +static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) +{ + VirtIOBlock *s = to_virtio_blk(vdev); + struct virtio_blk_config blkcfg; + + memcpy(&blkcfg, config, sizeof(blkcfg)); + bdrv_set_enable_write_cache(s->bs, blkcfg.wce != 0); +} + static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) { VirtIOBlock *s = to_virtio_blk(vdev); @@ -522,15 +533,29 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) features |= (1 << VIRTIO_BLK_F_BLK_SIZE); features |= (1 << VIRTIO_BLK_F_SCSI); + features |= (1 << VIRTIO_BLK_F_CONFIG_WCE); if (bdrv_enable_write_cache(s->bs)) - features |= (1 << VIRTIO_BLK_F_WCACHE); - + features |= (1 << VIRTIO_BLK_F_WCE); + if (bdrv_is_read_only(s->bs)) features |= 1 << VIRTIO_BLK_F_RO; return features; } +static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) +{ + VirtIOBlock *s = to_virtio_blk(vdev); + uint32_t features; + + if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { + return; + } + + features = vdev->guest_features; + bdrv_set_enable_write_cache(s->bs, !!(features & (1 << VIRTIO_BLK_F_WCE))); +} + static void virtio_blk_save(QEMUFile *f, void *opaque) { VirtIOBlock *s = opaque; @@ -609,7 +634,9 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk) sizeof(VirtIOBlock)); s->vdev.get_config = virtio_blk_update_config; + s->vdev.set_config = virtio_blk_set_config; s->vdev.get_features = virtio_blk_get_features; + s->vdev.set_status = virtio_blk_set_status; s->vdev.reset = virtio_blk_reset; s->bs = blk->conf.bs; s->conf = &blk->conf; diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h index 79ebccc95b..35834cf493 100644 --- a/hw/virtio-blk.h +++ b/hw/virtio-blk.h @@ -31,8 +31,9 @@ #define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ #define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ /* #define VIRTIO_BLK_F_IDENTIFY 8 ATA IDENTIFY supported, DEPRECATED */ -#define VIRTIO_BLK_F_WCACHE 9 /* write cache enabled */ +#define VIRTIO_BLK_F_WCE 9 /* write cache enabled */ #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ +#define VIRTIO_BLK_F_CONFIG_WCE 11 /* write cache configurable */ #define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ @@ -49,6 +50,7 @@ struct virtio_blk_config uint8_t alignment_offset; uint16_t min_io_size; uint32_t opt_io_size; + uint8_t wce; } QEMU_PACKED; /* These two define direction. */ diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 125eded9ca..5e6e09efb7 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -627,7 +627,7 @@ static int virtio_pci_set_guest_notifiers(void *opaque, bool assign) int r, n; /* Must unset vector notifier while guest notifier is still assigned */ - if (kvm_irqchip_in_kernel() && !assign) { + if (kvm_msi_via_irqfd_enabled() && !assign) { msix_unset_vector_notifiers(&proxy->pci_dev); g_free(proxy->vector_irqfd); proxy->vector_irqfd = NULL; @@ -645,7 +645,7 @@ static int virtio_pci_set_guest_notifiers(void *opaque, bool assign) } /* Must set vector notifier after guest notifier has been assigned */ - if (kvm_irqchip_in_kernel() && assign) { + if (kvm_msi_via_irqfd_enabled() && assign) { proxy->vector_irqfd = g_malloc0(sizeof(*proxy->vector_irqfd) * msix_nr_vectors_allocated(&proxy->pci_dev)); diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c index c4a5b22f94..5f737acd97 100644 --- a/hw/virtio-scsi.c +++ b/hw/virtio-scsi.c @@ -305,11 +305,17 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) goto incorrect_lun; } QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) { - if (r->tag == req->req.tmf->tag) { + VirtIOSCSIReq *cmd_req = r->hba_private; + if (cmd_req && cmd_req->req.cmd->tag == req->req.tmf->tag) { break; } } - if (r && r->hba_private) { + if (r) { + /* + * Assert that the request has not been completed yet, we + * check for it in the loop above. + */ + assert(r->hba_private); if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK) { /* "If the specified command is present in the task set, then * return a service response set to FUNCTION SUCCEEDED". diff --git a/hw/xilinx.h b/hw/xilinx.h index 7df21eb958..556c5aa9f1 100644 --- a/hw/xilinx.h +++ b/hw/xilinx.h @@ -1,3 +1,4 @@ +#include "stream.h" #include "qemu-common.h" #include "net.h" @@ -49,8 +50,8 @@ xilinx_ethlite_create(NICInfo *nd, target_phys_addr_t base, qemu_irq irq, } static inline DeviceState * -xilinx_axiethernet_create(void *dmach, - NICInfo *nd, target_phys_addr_t base, qemu_irq irq, +xilinx_axiethernet_create(NICInfo *nd, StreamSlave *peer, + target_phys_addr_t base, qemu_irq irq, int txmem, int rxmem) { DeviceState *dev; @@ -60,7 +61,7 @@ xilinx_axiethernet_create(void *dmach, qdev_set_nic_properties(dev, nd); qdev_prop_set_uint32(dev, "rxmem", rxmem); qdev_prop_set_uint32(dev, "txmem", txmem); - qdev_prop_set_ptr(dev, "dmach", dmach); + object_property_set_link(OBJECT(dev), OBJECT(peer), "tx_dev", NULL); qdev_init_nofail(dev); sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); @@ -68,21 +69,16 @@ xilinx_axiethernet_create(void *dmach, return dev; } -static inline DeviceState * -xilinx_axiethernetdma_create(void *dmach, - target_phys_addr_t base, qemu_irq irq, - qemu_irq irq2, int freqhz) +static inline void +xilinx_axiethernetdma_init(DeviceState *dev, StreamSlave *peer, + target_phys_addr_t base, qemu_irq irq, + qemu_irq irq2, int freqhz) { - DeviceState *dev = NULL; - - dev = qdev_create(NULL, "xlnx.axi-dma"); qdev_prop_set_uint32(dev, "freqhz", freqhz); - qdev_prop_set_ptr(dev, "dmach", dmach); + object_property_set_link(OBJECT(dev), OBJECT(peer), "tx_dev", NULL); qdev_init_nofail(dev); sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); sysbus_connect_irq(sysbus_from_qdev(dev), 1, irq2); - - return dev; } diff --git a/hw/xilinx_axidma.c b/hw/xilinx_axidma.c index f4bec37c7a..0e28c51738 100644 --- a/hw/xilinx_axidma.c +++ b/hw/xilinx_axidma.c @@ -29,7 +29,7 @@ #include "qemu-log.h" #include "qdev-addr.h" -#include "xilinx_axidma.h" +#include "stream.h" #define D(x) @@ -77,7 +77,7 @@ enum { SDESC_STATUS_COMPLETE = (1 << 31) }; -struct AXIStream { +struct Stream { QEMUBH *bh; ptimer_state *ptimer; qemu_irq irq; @@ -94,9 +94,9 @@ struct XilinxAXIDMA { SysBusDevice busdev; MemoryRegion iomem; uint32_t freqhz; - void *dmach; + StreamSlave *tx_dev; - struct AXIStream streams[2]; + struct Stream streams[2]; }; /* @@ -113,27 +113,27 @@ static inline int stream_desc_eof(struct SDesc *d) return d->control & SDESC_CTRL_EOF; } -static inline int stream_resetting(struct AXIStream *s) +static inline int stream_resetting(struct Stream *s) { return !!(s->regs[R_DMACR] & DMACR_RESET); } -static inline int stream_running(struct AXIStream *s) +static inline int stream_running(struct Stream *s) { return s->regs[R_DMACR] & DMACR_RUNSTOP; } -static inline int stream_halted(struct AXIStream *s) +static inline int stream_halted(struct Stream *s) { return s->regs[R_DMASR] & DMASR_HALTED; } -static inline int stream_idle(struct AXIStream *s) +static inline int stream_idle(struct Stream *s) { return !!(s->regs[R_DMASR] & DMASR_IDLE); } -static void stream_reset(struct AXIStream *s) +static void stream_reset(struct Stream *s) { s->regs[R_DMASR] = DMASR_HALTED; /* starts up halted. */ s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold. */ @@ -159,7 +159,7 @@ static void stream_desc_show(struct SDesc *d) } #endif -static void stream_desc_load(struct AXIStream *s, target_phys_addr_t addr) +static void stream_desc_load(struct Stream *s, target_phys_addr_t addr) { struct SDesc *d = &s->desc; int i; @@ -176,7 +176,7 @@ static void stream_desc_load(struct AXIStream *s, target_phys_addr_t addr) } } -static void stream_desc_store(struct AXIStream *s, target_phys_addr_t addr) +static void stream_desc_store(struct Stream *s, target_phys_addr_t addr) { struct SDesc *d = &s->desc; int i; @@ -192,7 +192,7 @@ static void stream_desc_store(struct AXIStream *s, target_phys_addr_t addr) cpu_physical_memory_write(addr, (void *) d, sizeof *d); } -static void stream_update_irq(struct AXIStream *s) +static void stream_update_irq(struct Stream *s) { unsigned int pending, mask, irq; @@ -204,7 +204,7 @@ static void stream_update_irq(struct AXIStream *s) qemu_set_irq(s->irq, !!irq); } -static void stream_reload_complete_cnt(struct AXIStream *s) +static void stream_reload_complete_cnt(struct Stream *s) { unsigned int comp_th; comp_th = (s->regs[R_DMACR] >> 16) & 0xff; @@ -213,14 +213,14 @@ static void stream_reload_complete_cnt(struct AXIStream *s) static void timer_hit(void *opaque) { - struct AXIStream *s = opaque; + struct Stream *s = opaque; stream_reload_complete_cnt(s); s->regs[R_DMASR] |= DMASR_DLY_IRQ; stream_update_irq(s); } -static void stream_complete(struct AXIStream *s) +static void stream_complete(struct Stream *s) { unsigned int comp_delay; @@ -240,8 +240,8 @@ static void stream_complete(struct AXIStream *s) } } -static void stream_process_mem2s(struct AXIStream *s, - struct XilinxDMAConnection *dmach) +static void stream_process_mem2s(struct Stream *s, + StreamSlave *tx_dev) { uint32_t prev_d; unsigned char txbuf[16 * 1024]; @@ -276,7 +276,7 @@ static void stream_process_mem2s(struct AXIStream *s, s->pos += txlen; if (stream_desc_eof(&s->desc)) { - xlx_dma_push_to_client(dmach, txbuf, s->pos, app); + stream_push(tx_dev, txbuf, s->pos, app); s->pos = 0; stream_complete(s); } @@ -295,7 +295,7 @@ static void stream_process_mem2s(struct AXIStream *s, } } -static void stream_process_s2mem(struct AXIStream *s, +static void stream_process_s2mem(struct Stream *s, unsigned char *buf, size_t len, uint32_t *app) { uint32_t prev_d; @@ -351,11 +351,11 @@ static void stream_process_s2mem(struct AXIStream *s, } } -static -void axidma_push(void *opaque, unsigned char *buf, size_t len, uint32_t *app) +static void +axidma_push(StreamSlave *obj, unsigned char *buf, size_t len, uint32_t *app) { - struct XilinxAXIDMA *d = opaque; - struct AXIStream *s = &d->streams[1]; + struct XilinxAXIDMA *d = FROM_SYSBUS(typeof(*d), SYS_BUS_DEVICE(obj)); + struct Stream *s = &d->streams[1]; if (!app) { hw_error("No stream app data!\n"); @@ -368,7 +368,7 @@ static uint64_t axidma_read(void *opaque, target_phys_addr_t addr, unsigned size) { struct XilinxAXIDMA *d = opaque; - struct AXIStream *s; + struct Stream *s; uint32_t r = 0; int sid; @@ -403,7 +403,7 @@ static void axidma_write(void *opaque, target_phys_addr_t addr, uint64_t value, unsigned size) { struct XilinxAXIDMA *d = opaque; - struct AXIStream *s; + struct Stream *s; int sid; sid = streamid_from_addr(addr); @@ -440,7 +440,7 @@ static void axidma_write(void *opaque, target_phys_addr_t addr, s->regs[addr] = value; s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle. */ if (!sid) { - stream_process_mem2s(s, d->dmach); + stream_process_mem2s(s, d->tx_dev); } break; default: @@ -466,12 +466,6 @@ static int xilinx_axidma_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->streams[0].irq); sysbus_init_irq(dev, &s->streams[1].irq); - if (!s->dmach) { - hw_error("Unconnected DMA channel.\n"); - } - - xlx_dma_connect_dma(s->dmach, s, axidma_push); - memory_region_init_io(&s->iomem, &axidma_ops, s, "xlnx.axi-dma", R_MAX * 4 * 2); sysbus_init_mmio(dev, &s->iomem); @@ -486,9 +480,16 @@ static int xilinx_axidma_init(SysBusDevice *dev) return 0; } +static void xilinx_axidma_initfn(Object *obj) +{ + struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); + + object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE, + (Object **) &s->tx_dev, NULL); +} + static Property axidma_properties[] = { DEFINE_PROP_UINT32("freqhz", struct XilinxAXIDMA, freqhz, 50000000), - DEFINE_PROP_PTR("dmach", struct XilinxAXIDMA, dmach), DEFINE_PROP_END_OF_LIST(), }; @@ -496,9 +497,11 @@ static void axidma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); k->init = xilinx_axidma_init; dc->props = axidma_properties; + ssc->push = axidma_push; } static TypeInfo axidma_info = { @@ -506,6 +509,11 @@ static TypeInfo axidma_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct XilinxAXIDMA), .class_init = axidma_class_init, + .instance_init = xilinx_axidma_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_STREAM_SLAVE }, + { } + } }; static void xilinx_axidma_register_types(void) diff --git a/hw/xilinx_axidma.h b/hw/xilinx_axidma.h deleted file mode 100644 index 37cb6f0911..0000000000 --- a/hw/xilinx_axidma.h +++ /dev/null @@ -1,39 +0,0 @@ -/* AXI DMA connection. Used until qdev provides a generic way. */ -typedef void (*DMAPushFn)(void *opaque, - unsigned char *buf, size_t len, uint32_t *app); - -struct XilinxDMAConnection { - void *dma; - void *client; - - DMAPushFn to_dma; - DMAPushFn to_client; -}; - -static inline void xlx_dma_connect_client(struct XilinxDMAConnection *dmach, - void *c, DMAPushFn f) -{ - dmach->client = c; - dmach->to_client = f; -} - -static inline void xlx_dma_connect_dma(struct XilinxDMAConnection *dmach, - void *d, DMAPushFn f) -{ - dmach->dma = d; - dmach->to_dma = f; -} - -static inline -void xlx_dma_push_to_dma(struct XilinxDMAConnection *dmach, - uint8_t *buf, size_t len, uint32_t *app) -{ - dmach->to_dma(dmach->dma, buf, len, app); -} -static inline -void xlx_dma_push_to_client(struct XilinxDMAConnection *dmach, - uint8_t *buf, size_t len, uint32_t *app) -{ - dmach->to_client(dmach->client, buf, len, app); -} - diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c index adfaf2c50e..eec155d440 100644 --- a/hw/xilinx_axienet.c +++ b/hw/xilinx_axienet.c @@ -28,7 +28,7 @@ #include "net.h" #include "net/checksum.h" -#include "xilinx_axidma.h" +#include "stream.h" #define DPHY(x) @@ -310,7 +310,7 @@ struct XilinxAXIEnet { SysBusDevice busdev; MemoryRegion iomem; qemu_irq irq; - void *dmach; + StreamSlave *tx_dev; NICState *nic; NICConf conf; @@ -648,7 +648,6 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) uint16_t csum16; int i; - s = s; DENET(qemu_log("%s: %zd bytes\n", __func__, size)); unicast = ~buf[0] & 0x1; @@ -773,7 +772,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) /* Good frame. */ app[2] |= 1 << 6; - xlx_dma_push_to_dma(s->dmach, (void *)s->rxmem, size, app); + stream_push(s->tx_dev, (void *)s->rxmem, size, app); s->regs[R_IS] |= IS_RX_COMPLETE; enet_update_irq(s); @@ -789,9 +788,9 @@ static void eth_cleanup(NetClientState *nc) } static void -axienet_stream_push(void *opaque, uint8_t *buf, size_t size, uint32_t *hdr) +axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr) { - struct XilinxAXIEnet *s = opaque; + struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); /* TX enable ? */ if (!(s->tc & TC_TX)) { @@ -845,12 +844,6 @@ static int xilinx_enet_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->irq); - if (!s->dmach) { - hw_error("Unconnected Xilinx Ethernet MAC.\n"); - } - - xlx_dma_connect_client(s->dmach, s, axienet_stream_push); - memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000); sysbus_init_mmio(dev, &s->iomem); @@ -870,11 +863,18 @@ static int xilinx_enet_init(SysBusDevice *dev) return 0; } +static void xilinx_enet_initfn(Object *obj) +{ + struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj)); + + object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE, + (Object **) &s->tx_dev, NULL); +} + static Property xilinx_enet_properties[] = { DEFINE_PROP_UINT32("phyaddr", struct XilinxAXIEnet, c_phyaddr, 7), DEFINE_PROP_UINT32("rxmem", struct XilinxAXIEnet, c_rxmem, 0x1000), DEFINE_PROP_UINT32("txmem", struct XilinxAXIEnet, c_txmem, 0x1000), - DEFINE_PROP_PTR("dmach", struct XilinxAXIEnet, dmach), DEFINE_NIC_PROPERTIES(struct XilinxAXIEnet, conf), DEFINE_PROP_END_OF_LIST(), }; @@ -883,9 +883,11 @@ static void xilinx_enet_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); k->init = xilinx_enet_init; dc->props = xilinx_enet_properties; + ssc->push = axienet_stream_push; } static TypeInfo xilinx_enet_info = { @@ -893,6 +895,11 @@ static TypeInfo xilinx_enet_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(struct XilinxAXIEnet), .class_init = xilinx_enet_class_init, + .instance_init = xilinx_enet_initfn, + .interfaces = (InterfaceInfo[]) { + { TYPE_STREAM_SLAVE }, + { } + } }; static void xilinx_enet_register_types(void) diff --git a/hw/xtensa_lx60.c b/hw/xtensa_lx60.c index c4f616f4fb..3653f65b1e 100644 --- a/hw/xtensa_lx60.c +++ b/hw/xtensa_lx60.c @@ -173,7 +173,7 @@ static void lx_init(const LxBoardDesc *board, int n; if (!cpu_model) { - cpu_model = "dc232b"; + cpu_model = XTENSA_DEFAULT_CPU_MODEL; } for (n = 0; n < smp_cpus; n++) { @@ -300,14 +300,14 @@ static void xtensa_lx200_init(ram_addr_t ram_size, static QEMUMachine xtensa_lx60_machine = { .name = "lx60", - .desc = "lx60 EVB (dc232b)", + .desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", .init = xtensa_lx60_init, .max_cpus = 4, }; static QEMUMachine xtensa_lx200_machine = { .name = "lx200", - .desc = "lx200 EVB (dc232b)", + .desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")", .init = xtensa_lx200_init, .max_cpus = 4, }; diff --git a/hw/xtensa_sim.c b/hw/xtensa_sim.c index 1ce07fb899..831460b7c4 100644 --- a/hw/xtensa_sim.c +++ b/hw/xtensa_sim.c @@ -102,7 +102,7 @@ static void xtensa_sim_init(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { if (!cpu_model) { - cpu_model = "dc232b"; + cpu_model = XTENSA_DEFAULT_CPU_MODEL; } sim_init(ram_size, boot_device, kernel_filename, kernel_cmdline, initrd_filename, cpu_model); @@ -110,7 +110,8 @@ static void xtensa_sim_init(ram_addr_t ram_size, static QEMUMachine xtensa_sim_machine = { .name = "sim", - .desc = "sim machine (dc232b)", + .desc = "sim machine (" XTENSA_DEFAULT_CPU_MODEL ")", + .is_default = true, .init = xtensa_sim_init, .max_cpus = 4, }; diff --git a/include/qemu/object.h b/include/qemu/object.h index 8b17776bb3..cc75feed66 100644 --- a/include/qemu/object.h +++ b/include/qemu/object.h @@ -239,6 +239,7 @@ struct ObjectClass { /*< private >*/ Type type; + GSList *interfaces; }; /** @@ -260,7 +261,6 @@ struct Object { /*< private >*/ ObjectClass *class; - GSList *interfaces; QTAILQ_HEAD(, ObjectProperty) properties; uint32_t ref; Object *parent; @@ -387,6 +387,16 @@ struct TypeInfo OBJECT_CLASS_CHECK(class, object_get_class(OBJECT(obj)), name) /** + * InterfaceInfo: + * @type: The name of the interface. + * + * The information associated with an interface. + */ +struct InterfaceInfo { + const char *type; +}; + +/** * InterfaceClass: * @parent_class: the base class * @@ -396,26 +406,30 @@ struct TypeInfo struct InterfaceClass { ObjectClass parent_class; + /*< private >*/ + ObjectClass *concrete_class; }; +#define TYPE_INTERFACE "interface" + /** - * InterfaceInfo: - * @type: The name of the interface. - * @interface_initfn: This method is called during class initialization and is - * used to initialize an interface associated with a class. This function - * should initialize any default virtual functions for a class and/or override - * virtual functions in a parent class. - * - * The information associated with an interface. + * INTERFACE_CLASS: + * @klass: class to cast from + * Returns: An #InterfaceClass or raise an error if cast is invalid */ -struct InterfaceInfo -{ - const char *type; +#define INTERFACE_CLASS(klass) \ + OBJECT_CLASS_CHECK(InterfaceClass, klass, TYPE_INTERFACE) - void (*interface_initfn)(ObjectClass *class, void *data); -}; - -#define TYPE_INTERFACE "interface" +/** + * INTERFACE_CHECK: + * @interface: the type to return + * @obj: the object to convert to an interface + * @name: the interface type name + * + * Returns: @obj casted to @interface if cast is valid, otherwise raise error. + */ +#define INTERFACE_CHECK(interface, obj, name) \ + ((interface *)object_dynamic_cast_assert(OBJECT((obj)), (name))) /** * object_new: diff --git a/include/qemu/page_cache.h b/include/qemu/page_cache.h new file mode 100644 index 0000000000..3839ac7726 --- /dev/null +++ b/include/qemu/page_cache.h @@ -0,0 +1,79 @@ +/* + * Page cache for QEMU + * The cache is base on a hash of the page address + * + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Orit Wasserman <owasserm@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef PAGE_CACHE_H +#define PAGE_CACHE_H + +/* Page cache for storing guest pages */ +typedef struct PageCache PageCache; + +/** + * cache_init: Initialize the page cache + * + * + * Returns new allocated cache or NULL on error + * + * @cache pointer to the PageCache struct + * @num_pages: cache maximal number of cached pages + * @page_size: cache page size + */ +PageCache *cache_init(int64_t num_pages, unsigned int page_size); + +/** + * cache_fini: free all cache resources + * @cache pointer to the PageCache struct + */ +void cache_fini(PageCache *cache); + +/** + * cache_is_cached: Checks to see if the page is cached + * + * Returns %true if page is cached + * + * @cache pointer to the PageCache struct + * @addr: page addr + */ +bool cache_is_cached(const PageCache *cache, uint64_t addr); + +/** + * get_cached_data: Get the data cached for an addr + * + * Returns pointer to the data cached or NULL if not cached + * + * @cache pointer to the PageCache struct + * @addr: page addr + */ +uint8_t *get_cached_data(const PageCache *cache, uint64_t addr); + +/** + * cache_insert: insert the page into the cache. the previous value will be overwritten + * + * @cache pointer to the PageCache struct + * @addr: page address + * @pdata: pointer to the page + */ +void cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata); + +/** + * cache_resize: resize the page cache. In case of size reduction the extra + * pages will be freed + * + * Returns -1 on error new cache size on success + * + * @cache pointer to the PageCache struct + * @num_pages: new page cache size (in pages) + */ +int64_t cache_resize(PageCache *cache, int64_t num_pages); + +#endif diff --git a/kvm-all.c b/kvm-all.c index 2148b20bdb..34b02c1fba 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -100,6 +100,10 @@ struct KVMState KVMState *kvm_state; bool kvm_kernel_irqchip; +bool kvm_async_interrupts_allowed; +bool kvm_irqfds_allowed; +bool kvm_msi_via_irqfd_allowed; +bool kvm_gsi_routing_allowed; static const KVMCapabilityInfo kvm_required_capabilites[] = { KVM_CAP_INFO(USER_MEMORY), @@ -852,18 +856,18 @@ static void kvm_handle_interrupt(CPUArchState *env, int mask) } } -int kvm_irqchip_set_irq(KVMState *s, int irq, int level) +int kvm_set_irq(KVMState *s, int irq, int level) { struct kvm_irq_level event; int ret; - assert(kvm_irqchip_in_kernel()); + assert(kvm_async_interrupts_enabled()); event.level = level; event.irq = irq; ret = kvm_vm_ioctl(s, s->irqchip_inject_ioctl, &event); if (ret < 0) { - perror("kvm_set_irqchip_line"); + perror("kvm_set_irq"); abort(); } @@ -1088,7 +1092,7 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg) assert(route->kroute.type == KVM_IRQ_ROUTING_MSI); - return kvm_irqchip_set_irq(s, route->kroute.gsi, 1); + return kvm_set_irq(s, route->kroute.gsi, 1); } int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) @@ -1096,7 +1100,7 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) struct kvm_irq_routing_entry kroute; int virq; - if (!kvm_irqchip_in_kernel()) { + if (!kvm_gsi_routing_enabled()) { return -ENOSYS; } @@ -1125,7 +1129,7 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign) .flags = assign ? 0 : KVM_IRQFD_FLAG_DEASSIGN, }; - if (!kvm_irqchip_in_kernel()) { + if (!kvm_irqfds_enabled()) { return -ENOSYS; } @@ -1201,12 +1205,36 @@ static int kvm_irqchip_create(KVMState *s) s->irqchip_inject_ioctl = KVM_IRQ_LINE_STATUS; } kvm_kernel_irqchip = true; + /* If we have an in-kernel IRQ chip then we must have asynchronous + * interrupt delivery (though the reverse is not necessarily true) + */ + kvm_async_interrupts_allowed = true; kvm_init_irq_routing(s); return 0; } +static int kvm_max_vcpus(KVMState *s) +{ + int ret; + + /* Find number of supported CPUs using the recommended + * procedure from the kernel API documentation to cope with + * older kernels that may be missing capabilities. + */ + ret = kvm_check_extension(s, KVM_CAP_MAX_VCPUS); + if (ret) { + return ret; + } + ret = kvm_check_extension(s, KVM_CAP_NR_VCPUS); + if (ret) { + return ret; + } + + return 4; +} + int kvm_init(void) { static const char upgrade_note[] = @@ -1216,6 +1244,7 @@ int kvm_init(void) const KVMCapabilityInfo *missing_cap; int ret; int i; + int max_vcpus; s = g_malloc0(sizeof(KVMState)); @@ -1256,6 +1285,14 @@ int kvm_init(void) goto err; } + max_vcpus = kvm_max_vcpus(s); + if (smp_cpus > max_vcpus) { + ret = -EINVAL; + fprintf(stderr, "Number of SMP cpus requested (%d) exceeds max cpus " + "supported by KVM (%d)\n", smp_cpus, max_vcpus); + goto err; + } + s->vmfd = kvm_ioctl(s, KVM_CREATE_VM, 0); if (s->vmfd < 0) { #ifdef TARGET_S390X @@ -1667,11 +1704,6 @@ int kvm_has_gsi_routing(void) #endif } -int kvm_allows_irq0_override(void) -{ - return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing(); -} - void *kvm_vmalloc(ram_addr_t size) { #ifdef TARGET_S390X diff --git a/kvm-stub.c b/kvm-stub.c index d23b11c020..94c9ea15b0 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -19,6 +19,10 @@ KVMState *kvm_state; bool kvm_kernel_irqchip; +bool kvm_async_interrupts_allowed; +bool kvm_irqfds_allowed; +bool kvm_msi_via_irqfd_allowed; +bool kvm_gsi_routing_allowed; int kvm_init_vcpu(CPUArchState *env) { @@ -71,11 +75,6 @@ int kvm_has_many_ioeventfds(void) return 0; } -int kvm_allows_irq0_override(void) -{ - return 1; -} - int kvm_has_pit_state2(void) { return 0; diff --git a/kvm.h b/kvm.h index 2617dd5acd..5b8f588813 100644 --- a/kvm.h +++ b/kvm.h @@ -24,13 +24,69 @@ extern int kvm_allowed; extern bool kvm_kernel_irqchip; +extern bool kvm_async_interrupts_allowed; +extern bool kvm_irqfds_allowed; +extern bool kvm_msi_via_irqfd_allowed; +extern bool kvm_gsi_routing_allowed; #if defined CONFIG_KVM || !defined NEED_CPU_H #define kvm_enabled() (kvm_allowed) +/** + * kvm_irqchip_in_kernel: + * + * Returns: true if the user asked us to create an in-kernel + * irqchip via the "kernel_irqchip=on" machine option. + * What this actually means is architecture and machine model + * specific: on PC, for instance, it means that the LAPIC, + * IOAPIC and PIT are all in kernel. This function should never + * be used from generic target-independent code: use one of the + * following functions or some other specific check instead. + */ #define kvm_irqchip_in_kernel() (kvm_kernel_irqchip) + +/** + * kvm_async_interrupts_enabled: + * + * Returns: true if we can deliver interrupts to KVM + * asynchronously (ie by ioctl from any thread at any time) + * rather than having to do interrupt delivery synchronously + * (where the vcpu must be stopped at a suitable point first). + */ +#define kvm_async_interrupts_enabled() (kvm_async_interrupts_allowed) + +/** + * kvm_irqfds_enabled: + * + * Returns: true if we can use irqfds to inject interrupts into + * a KVM CPU (ie the kernel supports irqfds and we are running + * with a configuration where it is meaningful to use them). + */ +#define kvm_irqfds_enabled() (kvm_irqfds_allowed) + +/** + * kvm_msi_via_irqfd_enabled: + * + * Returns: true if we can route a PCI MSI (Message Signaled Interrupt) + * to a KVM CPU via an irqfd. This requires that the kernel supports + * this and that we're running in a configuration that permits it. + */ +#define kvm_msi_via_irqfd_enabled() (kvm_msi_via_irqfd_allowed) + +/** + * kvm_gsi_routing_enabled: + * + * Returns: true if GSI routing is enabled (ie the kernel supports + * it and we're running in a configuration that permits it). + */ +#define kvm_gsi_routing_enabled() (kvm_gsi_routing_allowed) + #else #define kvm_enabled() (0) #define kvm_irqchip_in_kernel() (false) +#define kvm_async_interrupts_enabled() (false) +#define kvm_irqfds_enabled() (false) +#define kvm_msi_via_irqfd_enabled() (false) +#define kvm_gsi_routing_allowed() (false) #endif struct kvm_run; @@ -62,8 +118,6 @@ int kvm_has_pit_state2(void); int kvm_has_many_ioeventfds(void); int kvm_has_gsi_routing(void); -int kvm_allows_irq0_override(void); - #ifdef NEED_CPU_H int kvm_init_vcpu(CPUArchState *env); @@ -133,7 +187,7 @@ int kvm_arch_on_sigbus(int code, void *addr); void kvm_arch_init_irq_routing(KVMState *s); -int kvm_irqchip_set_irq(KVMState *s, int irq, int level); +int kvm_set_irq(KVMState *s, int irq, int level); int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg); void kvm_irqchip_add_irq_route(KVMState *s, int gsi, int irqchip, int pin); diff --git a/linux-user/main.c b/linux-user/main.c index 53714de0d4..9d921aa4f0 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -958,7 +958,8 @@ void cpu_loop(CPUUniCore32State *env) } } break; - case UC32_EXCP_TRAP: + case UC32_EXCP_DTRAP: + case UC32_EXCP_ITRAP: info.si_signo = SIGSEGV; info.si_errno = 0; /* XXX: check env->error_code */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 9be5ac0788..78691473fa 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1844,7 +1844,7 @@ typedef struct { } __siginfo_t; typedef struct { - unsigned long si_float_regs [32]; + abi_ulong si_float_regs[32]; unsigned long si_fsr; unsigned long si_fpqdepth; struct { @@ -2056,11 +2056,9 @@ restore_fpu_state(CPUSPARCState *env, qemu_siginfo_fpu_t *fpu) return -EFAULT; #endif -#if 0 /* XXX: incorrect */ - err = __copy_from_user(&env->fpr[0], &fpu->si_float_regs[0], - (sizeof(unsigned long) * 32)); -#endif + err = copy_from_user(&env->fpr[0], fpu->si_float_regs[0], + (sizeof(abi_ulong) * 32)); err |= __get_user(env->fsr, &fpu->si_fsr); #if 0 err |= __get_user(current->thread.fpqdepth, &fpu->si_fpqdepth); diff --git a/migration-tcp.c b/migration-tcp.c index 440804db75..ac891c38a3 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -82,27 +82,23 @@ static void tcp_wait_for_connect(void *opaque) int tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp) { + bool in_progress; + s->get_error = socket_errno; s->write = socket_write; s->close = tcp_close; - s->fd = inet_connect(host_port, false, errp); + s->fd = inet_connect(host_port, false, &in_progress, errp); + if (error_is_set(errp)) { + migrate_fd_error(s); + return -1; + } - if (!error_is_set(errp)) { - migrate_fd_connect(s); - } else if (error_is_type(*errp, QERR_SOCKET_CONNECT_IN_PROGRESS)) { + if (in_progress) { DPRINTF("connect in progress\n"); qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s); - } else if (error_is_type(*errp, QERR_SOCKET_CREATE_FAILED)) { - DPRINTF("connect failed\n"); - return -1; - } else if (error_is_type(*errp, QERR_SOCKET_CONNECT_FAILED)) { - DPRINTF("connect failed\n"); - migrate_fd_error(s); - return -1; } else { - DPRINTF("unknown error\n"); - return -1; + migrate_fd_connect(s); } return 0; diff --git a/migration.c b/migration.c index 8db1b433f0..653a3c1a88 100644 --- a/migration.c +++ b/migration.c @@ -43,6 +43,9 @@ enum { #define MAX_THROTTLE (32 << 20) /* Migration speed throttling */ +/* Migration XBZRLE default cache size */ +#define DEFAULT_MIGRATE_CACHE_SIZE (64 * 1024 * 1024) + static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); @@ -55,6 +58,7 @@ static MigrationState *migrate_get_current(void) static MigrationState current_migration = { .state = MIG_STATE_SETUP, .bandwidth_limit = MAX_THROTTLE, + .xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE, }; return ¤t_migration; @@ -113,6 +117,43 @@ uint64_t migrate_max_downtime(void) return max_downtime; } +MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) +{ + MigrationCapabilityStatusList *head = NULL; + MigrationCapabilityStatusList *caps; + MigrationState *s = migrate_get_current(); + int i; + + for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) { + if (head == NULL) { + head = g_malloc0(sizeof(*caps)); + caps = head; + } else { + caps->next = g_malloc0(sizeof(*caps)); + caps = caps->next; + } + caps->value = + g_malloc(sizeof(*caps->value)); + caps->value->capability = i; + caps->value->state = s->enabled_capabilities[i]; + } + + return head; +} + +static void get_xbzrle_cache_stats(MigrationInfo *info) +{ + if (migrate_use_xbzrle()) { + info->has_xbzrle_cache = true; + info->xbzrle_cache = g_malloc0(sizeof(*info->xbzrle_cache)); + info->xbzrle_cache->cache_size = migrate_xbzrle_cache_size(); + info->xbzrle_cache->bytes = xbzrle_mig_bytes_transferred(); + info->xbzrle_cache->pages = xbzrle_mig_pages_transferred(); + info->xbzrle_cache->cache_miss = xbzrle_mig_pages_cache_miss(); + info->xbzrle_cache->overflow = xbzrle_mig_pages_overflow(); + } +} + MigrationInfo *qmp_query_migrate(Error **errp) { MigrationInfo *info = g_malloc0(sizeof(*info)); @@ -133,6 +174,9 @@ MigrationInfo *qmp_query_migrate(Error **errp) info->ram->total = ram_bytes_total(); info->ram->total_time = qemu_get_clock_ms(rt_clock) - s->total_time; + info->ram->duplicate = dup_mig_pages_transferred(); + info->ram->normal = norm_mig_pages_transferred(); + info->ram->normal_bytes = norm_mig_bytes_transferred(); if (blk_mig_active()) { info->has_disk = true; @@ -141,8 +185,12 @@ MigrationInfo *qmp_query_migrate(Error **errp) info->disk->remaining = blk_mig_bytes_remaining(); info->disk->total = blk_mig_bytes_total(); } + + get_xbzrle_cache_stats(info); break; case MIG_STATE_COMPLETED: + get_xbzrle_cache_stats(info); + info->has_status = true; info->status = g_strdup("completed"); @@ -152,6 +200,9 @@ MigrationInfo *qmp_query_migrate(Error **errp) info->ram->remaining = 0; info->ram->total = ram_bytes_total(); info->ram->total_time = s->total_time; + info->ram->duplicate = dup_mig_pages_transferred(); + info->ram->normal = norm_mig_pages_transferred(); + info->ram->normal_bytes = norm_mig_bytes_transferred(); break; case MIG_STATE_ERROR: info->has_status = true; @@ -166,6 +217,22 @@ MigrationInfo *qmp_query_migrate(Error **errp) return info; } +void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, + Error **errp) +{ + MigrationState *s = migrate_get_current(); + MigrationCapabilityStatusList *cap; + + if (s->state == MIG_STATE_ACTIVE) { + error_set(errp, QERR_MIGRATION_ACTIVE); + return; + } + + for (cap = params; cap; cap = cap->next) { + s->enabled_capabilities[cap->value->capability] = cap->value->state; + } +} + /* shared migration helpers */ static int migrate_fd_cleanup(MigrationState *s) @@ -375,10 +442,18 @@ static MigrationState *migrate_init(const MigrationParams *params) { MigrationState *s = migrate_get_current(); int64_t bandwidth_limit = s->bandwidth_limit; + bool enabled_capabilities[MIGRATION_CAPABILITY_MAX]; + int64_t xbzrle_cache_size = s->xbzrle_cache_size; + + memcpy(enabled_capabilities, s->enabled_capabilities, + sizeof(enabled_capabilities)); memset(s, 0, sizeof(*s)); s->bandwidth_limit = bandwidth_limit; s->params = *params; + memcpy(s->enabled_capabilities, enabled_capabilities, + sizeof(enabled_capabilities)); + s->xbzrle_cache_size = xbzrle_cache_size; s->bandwidth_limit = bandwidth_limit; s->state = MIG_STATE_SETUP; @@ -459,6 +534,25 @@ void qmp_migrate_cancel(Error **errp) migrate_fd_cancel(migrate_get_current()); } +void qmp_migrate_set_cache_size(int64_t value, Error **errp) +{ + MigrationState *s = migrate_get_current(); + + /* Check for truncation */ + if (value != (size_t)value) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cache size", + "exceeding address space"); + return; + } + + s->xbzrle_cache_size = xbzrle_cache_resize(value); +} + +int64_t qmp_query_migrate_cache_size(Error **errp) +{ + return migrate_xbzrle_cache_size(); +} + void qmp_migrate_set_speed(int64_t value, Error **errp) { MigrationState *s; @@ -478,3 +572,21 @@ void qmp_migrate_set_downtime(double value, Error **errp) value = MAX(0, MIN(UINT64_MAX, value)); max_downtime = (uint64_t)value; } + +int migrate_use_xbzrle(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[MIGRATION_CAPABILITY_XBZRLE]; +} + +int64_t migrate_xbzrle_cache_size(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->xbzrle_cache_size; +} diff --git a/migration.h b/migration.h index 57572a61e9..a9852fcae0 100644 --- a/migration.h +++ b/migration.h @@ -19,6 +19,7 @@ #include "notify.h" #include "error.h" #include "vmstate.h" +#include "qapi-types.h" struct MigrationParams { bool blk; @@ -39,6 +40,8 @@ struct MigrationState void *opaque; MigrationParams params; int64_t total_time; + bool enabled_capabilities[MIGRATION_CAPABILITY_MAX]; + int64_t xbzrle_cache_size; }; void process_incoming_migration(QEMUFile *f); @@ -84,6 +87,15 @@ uint64_t ram_bytes_total(void); extern SaveVMHandlers savevm_ram_handlers; +uint64_t dup_mig_bytes_transferred(void); +uint64_t dup_mig_pages_transferred(void); +uint64_t norm_mig_bytes_transferred(void); +uint64_t norm_mig_pages_transferred(void); +uint64_t xbzrle_mig_bytes_transferred(void); +uint64_t xbzrle_mig_pages_transferred(void); +uint64_t xbzrle_mig_pages_overflow(void); +uint64_t xbzrle_mig_pages_cache_miss(void); + /** * @migrate_add_blocker - prevent migration from proceeding * @@ -98,4 +110,13 @@ void migrate_add_blocker(Error *reason); */ void migrate_del_blocker(Error *reason); +int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen, + uint8_t *dst, int dlen); +int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen); + +int migrate_use_xbzrle(void); +int64_t migrate_xbzrle_cache_size(void); + +int64_t xbzrle_cache_resize(int64_t new_size); + #endif diff --git a/monitor.c b/monitor.c index 49dccfe854..dd63f1d640 100644 --- a/monitor.c +++ b/monitor.c @@ -172,41 +172,11 @@ struct Monitor { CPUArchState *mon_cpu; BlockDriverCompletionFunc *password_completion_cb; void *password_opaque; -#ifdef CONFIG_DEBUG_MONITOR - int print_calls_nr; -#endif QError *error; QLIST_HEAD(,mon_fd_t) fds; QLIST_ENTRY(Monitor) entry; }; -#ifdef CONFIG_DEBUG_MONITOR -#define MON_DEBUG(fmt, ...) do { \ - fprintf(stderr, "Monitor: "); \ - fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) - -static inline void mon_print_count_inc(Monitor *mon) -{ - mon->print_calls_nr++; -} - -static inline void mon_print_count_init(Monitor *mon) -{ - mon->print_calls_nr = 0; -} - -static inline int mon_print_count_get(const Monitor *mon) -{ - return mon->print_calls_nr; -} - -#else /* !CONFIG_DEBUG_MONITOR */ -#define MON_DEBUG(fmt, ...) do { } while (0) -static inline void mon_print_count_inc(Monitor *mon) { } -static inline void mon_print_count_init(Monitor *mon) { } -static inline int mon_print_count_get(const Monitor *mon) { return 0; } -#endif /* CONFIG_DEBUG_MONITOR */ - /* QMP checker flags */ #define QMP_ACCEPT_UNKNOWNS 1 @@ -299,8 +269,6 @@ void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) if (!mon) return; - mon_print_count_inc(mon); - if (monitor_ctrl_mode(mon)) { return; } @@ -385,16 +353,26 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data) QDECREF(json); } +static QDict *build_qmp_error_dict(const QError *err) +{ + QObject *obj; + + obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %p } }", + ErrorClass_lookup[err->err_class], + qerror_human(err)); + + return qobject_to_qdict(obj); +} + static void monitor_protocol_emitter(Monitor *mon, QObject *data) { QDict *qmp; trace_monitor_protocol_emitter(mon); - qmp = qdict_new(); - if (!monitor_has_error(mon)) { /* success response */ + qmp = qdict_new(); if (data) { qobject_incref(data); qdict_put_obj(qmp, "return", data); @@ -404,9 +382,7 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data) } } else { /* error response */ - qdict_put(mon->error->error, "desc", qerror_human(mon->error)); - qdict_put(qmp, "error", mon->error->error); - QINCREF(mon->error->error); + qmp = build_qmp_error_dict(mon->error); QDECREF(mon->error); mon->error = NULL; } @@ -456,6 +432,7 @@ static const char *monitor_event_names[] = { [QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED", [QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED", [QEVENT_SUSPEND] = "SUSPEND", + [QEVENT_SUSPEND_DISK] = "SUSPEND_DISK", [QEVENT_WAKEUP] = "WAKEUP", [QEVENT_BALLOON_CHANGE] = "BALLOON_CHANGE", }; @@ -2655,6 +2632,20 @@ static mon_cmd_t info_cmds[] = { .mhandler.info = hmp_info_migrate, }, { + .name = "migrate_capabilities", + .args_type = "", + .params = "", + .help = "show current migration capabilities", + .mhandler.info = hmp_info_migrate_capabilities, + }, + { + .name = "migrate_cache_size", + .args_type = "", + .params = "", + .help = "show current migration xbzrle cache size", + .mhandler.info = hmp_info_migrate_cache_size, + }, + { .name = "balloon", .args_type = "", .params = "", @@ -3860,8 +3851,6 @@ void monitor_set_error(Monitor *mon, QError *qerror) if (!mon->error) { mon->error = qerror; } else { - MON_DEBUG("Additional error report at %s:%d\n", - qerror->file, qerror->linenr); QDECREF(qerror); } } @@ -3875,36 +3864,7 @@ static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret) * Action: Report an internal error to the client if in QMP. */ qerror_report(QERR_UNDEFINED_ERROR); - MON_DEBUG("command '%s' returned failure but did not pass an error\n", - cmd->name); } - -#ifdef CONFIG_DEBUG_MONITOR - if (!ret && monitor_has_error(mon)) { - /* - * If it returns success, it must not have passed an error. - * - * Action: Report the passed error to the client. - */ - MON_DEBUG("command '%s' returned success but passed an error\n", - cmd->name); - } - - if (mon_print_count_get(mon) > 0 && strcmp(cmd->name, "info") != 0) { - /* - * Handlers should not call Monitor print functions. - * - * Action: Ignore them in QMP. - * - * (XXX: we don't check any 'info' or 'query' command here - * because the user print function _is_ called by do_info(), hence - * we will trigger this check. This problem will go away when we - * make 'query' commands real and kill do_info()) - */ - MON_DEBUG("command '%s' called print functions %d time(s)\n", - cmd->name, mon_print_count_get(mon)); - } -#endif } static void handle_user_command(Monitor *mon, const char *cmdline) @@ -4433,8 +4393,6 @@ static void qmp_call_cmd(Monitor *mon, const mon_cmd_t *cmd, int ret; QObject *data = NULL; - mon_print_count_init(mon); - ret = cmd->mhandler.cmd_new(mon, params, &data); handler_audit(mon, cmd, ret); monitor_protocol_emitter(mon, data); diff --git a/monitor.h b/monitor.h index 5f4de1b3da..4ef9a046f8 100644 --- a/monitor.h +++ b/monitor.h @@ -40,6 +40,7 @@ typedef enum MonitorEvent { QEVENT_BLOCK_JOB_CANCELLED, QEVENT_DEVICE_TRAY_MOVED, QEVENT_SUSPEND, + QEVENT_SUSPEND_DISK, QEVENT_WAKEUP, QEVENT_BALLOON_CHANGE, diff --git a/nbd.c b/nbd.c index dc0adf90ed..0dd60c5f4c 100644 --- a/nbd.c +++ b/nbd.c @@ -162,7 +162,7 @@ int tcp_socket_outgoing(const char *address, uint16_t port) int tcp_socket_outgoing_spec(const char *address_and_port) { - return inet_connect(address_and_port, true, NULL); + return inet_connect(address_and_port, true, NULL, NULL); } int tcp_socket_incoming(const char *address, uint16_t port) diff --git a/net/slirp.c b/net/slirp.c index 08adb97da5..8db66ea539 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -718,9 +718,9 @@ int net_init_slirp(const NetClientOptions *opts, const char *name, net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD); net_init_slirp_configs(user->guestfwd, 0); - ret = net_slirp_init(peer, "user", name, user->restrict, vnet, user->host, - user->hostname, user->tftp, user->bootfile, - user->dhcpstart, user->dns, user->smb, + ret = net_slirp_init(peer, "user", name, user->q_restrict, vnet, + user->host, user->hostname, user->tftp, + user->bootfile, user->dhcpstart, user->dns, user->smb, user->smbserver); while (slirp_configs) { diff --git a/page_cache.c b/page_cache.c new file mode 100644 index 0000000000..0294f7e9f6 --- /dev/null +++ b/page_cache.c @@ -0,0 +1,218 @@ +/* + * Page cache for QEMU + * The cache is base on a hash of the page address + * + * Copyright 2012 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Orit Wasserman <owasserm@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 <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <string.h> +#include <sys/time.h> +#include <sys/types.h> +#include <stdbool.h> +#include <glib.h> +#include <strings.h> + +#include "qemu-common.h" +#include "qemu/page_cache.h" + +#ifdef DEBUG_CACHE +#define DPRINTF(fmt, ...) \ + do { fprintf(stdout, "cache: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +typedef struct CacheItem CacheItem; + +struct CacheItem { + uint64_t it_addr; + uint64_t it_age; + uint8_t *it_data; +}; + +struct PageCache { + CacheItem *page_cache; + unsigned int page_size; + int64_t max_num_items; + uint64_t max_item_age; + int64_t num_items; +}; + +PageCache *cache_init(int64_t num_pages, unsigned int page_size) +{ + int64_t i; + + PageCache *cache; + + if (num_pages <= 0) { + DPRINTF("invalid number of pages\n"); + return NULL; + } + + cache = g_malloc(sizeof(*cache)); + + /* round down to the nearest power of 2 */ + if (!is_power_of_2(num_pages)) { + num_pages = pow2floor(num_pages); + DPRINTF("rounding down to %" PRId64 "\n", num_pages); + } + cache->page_size = page_size; + cache->num_items = 0; + cache->max_item_age = 0; + cache->max_num_items = num_pages; + + DPRINTF("Setting cache buckets to %" PRId64 "\n", cache->max_num_items); + + cache->page_cache = g_malloc((cache->max_num_items) * + sizeof(*cache->page_cache)); + + for (i = 0; i < cache->max_num_items; i++) { + cache->page_cache[i].it_data = NULL; + cache->page_cache[i].it_age = 0; + cache->page_cache[i].it_addr = -1; + } + + return cache; +} + +void cache_fini(PageCache *cache) +{ + int64_t i; + + g_assert(cache); + g_assert(cache->page_cache); + + for (i = 0; i < cache->max_num_items; i++) { + g_free(cache->page_cache[i].it_data); + } + + g_free(cache->page_cache); + cache->page_cache = NULL; +} + +static size_t cache_get_cache_pos(const PageCache *cache, + uint64_t address) +{ + size_t pos; + + g_assert(cache->max_num_items); + pos = (address / cache->page_size) & (cache->max_num_items - 1); + return pos; +} + +bool cache_is_cached(const PageCache *cache, uint64_t addr) +{ + size_t pos; + + g_assert(cache); + g_assert(cache->page_cache); + + pos = cache_get_cache_pos(cache, addr); + + return (cache->page_cache[pos].it_addr == addr); +} + +static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr) +{ + size_t pos; + + g_assert(cache); + g_assert(cache->page_cache); + + pos = cache_get_cache_pos(cache, addr); + + return &cache->page_cache[pos]; +} + +uint8_t *get_cached_data(const PageCache *cache, uint64_t addr) +{ + return cache_get_by_addr(cache, addr)->it_data; +} + +void cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata) +{ + + CacheItem *it = NULL; + + g_assert(cache); + g_assert(cache->page_cache); + + /* actual update of entry */ + it = cache_get_by_addr(cache, addr); + + if (!it->it_data) { + cache->num_items++; + } + + it->it_data = pdata; + it->it_age = ++cache->max_item_age; + it->it_addr = addr; +} + +int64_t cache_resize(PageCache *cache, int64_t new_num_pages) +{ + PageCache *new_cache; + int64_t i; + + CacheItem *old_it, *new_it; + + g_assert(cache); + + /* cache was not inited */ + if (cache->page_cache == NULL) { + return -1; + } + + /* same size */ + if (pow2floor(new_num_pages) == cache->max_num_items) { + return cache->max_num_items; + } + + new_cache = cache_init(new_num_pages, cache->page_size); + if (!(new_cache)) { + DPRINTF("Error creating new cache\n"); + return -1; + } + + /* move all data from old cache */ + for (i = 0; i < cache->max_num_items; i++) { + old_it = &cache->page_cache[i]; + if (old_it->it_addr != -1) { + /* check for collision, if there is, keep MRU page */ + new_it = cache_get_by_addr(new_cache, old_it->it_addr); + if (new_it->it_data) { + /* keep the MRU page */ + if (new_it->it_age >= old_it->it_age) { + g_free(old_it->it_data); + } else { + g_free(new_it->it_data); + new_it->it_data = old_it->it_data; + new_it->it_age = old_it->it_age; + new_it->it_addr = old_it->it_addr; + } + } else { + cache_insert(new_cache, old_it->it_addr, old_it->it_data); + } + } + } + + cache->page_cache = new_cache->page_cache; + cache->max_num_items = new_cache->max_num_items; + cache->num_items = new_cache->num_items; + + g_free(new_cache); + + return cache->max_num_items; +} diff --git a/qapi-schema.json b/qapi-schema.json index cddf63a878..53bbe46e4d 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3,6 +3,36 @@ # QAPI Schema ## +# @ErrorClass +# +# QEMU error classes +# +# @GenericError: this is used for errors that don't require a specific error +# class. This should be the default case for most errors +# +# @CommandNotFound: the requested command has not been found +# +# @DeviceEncrypted: the requested operation can't be fulfilled because the +# selected device is encrypted +# +# @DeviceNotActive: a device has failed to be become active +# +# @DeviceNotFound: the requested device has not been found +# +# @KVMMissingCap: the requested operation can't be fulfilled because a +# required KVM capability is missing +# +# @MigrationExpected: the requested operation can't be fulfilled because a +# migration process is expected +# +# Since: 1.2 +## +{ 'enum': 'ErrorClass', + 'data': [ 'GenericError', 'CommandNotFound', 'DeviceEncrypted', + 'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap', + 'MigrationExpected' ] } + +## # @NameInfo: # # Guest name information. @@ -260,15 +290,43 @@ # # @total: total amount of bytes involved in the migration process # -# @total_time: tota0l amount of ms since migration started. If +# @total-time: total amount of ms since migration started. If # migration has ended, it returns the total migration # time. (since 1.2) # -# Since: 0.14.0. +# @duplicate: number of duplicate pages (since 1.2) +# +# @normal : number of normal pages (since 1.2) +# +# @normal-bytes : number of normal bytes sent (since 1.2) +# +# Since: 0.14.0 ## { 'type': 'MigrationStats', 'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' , - 'total_time': 'int' } } + 'total-time': 'int', 'duplicate': 'int', 'normal': 'int', + 'normal-bytes': 'int' } } + +## +# @XBZRLECacheStats +# +# Detailed XBZRLE migration cache statistics +# +# @cache-size: XBZRLE cache size +# +# @bytes: amount of bytes already transferred to the target VM +# +# @pages: amount of pages transferred to the target VM +# +# @cache-miss: number of cache miss +# +# @overflow: number of overflows +# +# Since: 1.2 +## +{ 'type': 'XBZRLECacheStats', + 'data': {'cache-size': 'int', 'bytes': 'int', 'pages': 'int', + 'cache-miss': 'int', 'overflow': 'int' } } ## # @MigrationInfo @@ -288,11 +346,16 @@ # status, only returned if status is 'active' and it is a block # migration # +# @xbzrle-cache: #optional @XBZRLECacheStats containing detailed XBZRLE +# migration statistics, only returned if XBZRLE feature is on and +# status is 'active' or 'completed' (since 1.2) +# # Since: 0.14.0 ## { 'type': 'MigrationInfo', 'data': {'*status': 'str', '*ram': 'MigrationStats', - '*disk': 'MigrationStats'} } + '*disk': 'MigrationStats', + '*xbzrle-cache': 'XBZRLECacheStats'} } ## # @query-migrate @@ -306,6 +369,57 @@ { 'command': 'query-migrate', 'returns': 'MigrationInfo' } ## +# @MigrationCapability +# +# Migration capabilities enumeration +# +# @xbzrle: Migration supports xbzrle (Xor Based Zero Run Length Encoding). +# This feature allows us to minimize migration traffic for certain work +# loads, by sending compressed difference of the pages +# +# Since: 1.2 +## +{ 'enum': 'MigrationCapability', + 'data': ['xbzrle'] } + +## +# @MigrationCapabilityStatus +# +# Migration capability information +# +# @capability: capability enum +# +# @state: capability state bool +# +# Since: 1.2 +## +{ 'type': 'MigrationCapabilityStatus', + 'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } } + +## +# @migrate-set-capabilities +# +# Enable/Disable the following migration capabilities (like xbzrle) +# +# @capabilities: json array of capability modifications to make +# +# Since: 1.2 +## +{ 'command': 'migrate-set-capabilities', + 'data': { 'capabilities': ['MigrationCapabilityStatus'] } } + +## +# @query-migrate-capabilities +# +# Returns information about the current migration capabilities status +# +# Returns: @MigrationCapabilitiesStatus +# +# Since: 1.2 +## +{ 'command': 'query-migrate-capabilities', 'returns': ['MigrationCapabilityStatus']} + +## # @MouseInfo: # # Information about a mouse device. @@ -402,6 +516,9 @@ # # @encrypted: true if the backing device is encrypted # +# @encryption_key_missing: true if the backing device is encrypted but an +# valid encryption key is missing +# # @bps: total throughput limit in bytes per second is specified # # @bps_rd: read throughput limit in bytes per second is specified @@ -421,9 +538,9 @@ { 'type': 'BlockDeviceInfo', 'data': { 'file': 'str', 'ro': 'bool', 'drv': 'str', '*backing_file': 'str', 'backing_file_depth': 'int', - 'encrypted': 'bool', 'bps': 'int', 'bps_rd': 'int', - 'bps_wr': 'int', 'iops': 'int', 'iops_rd': 'int', - 'iops_wr': 'int'} } + 'encrypted': 'bool', 'encryption_key_missing': 'bool', + 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', + 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int'} } ## # @BlockDeviceIoStatus: @@ -625,7 +742,6 @@ # Returns information about the current VNC server # # Returns: @VncInfo -# If VNC support is not compiled in, FeatureDisabled # # Since: 0.14.0 ## @@ -1009,9 +1125,6 @@ # 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 # @@ -1032,8 +1145,6 @@ # @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 # @@ -1075,7 +1186,6 @@ # 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 # @@ -1126,7 +1236,6 @@ # 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 @@ -1167,11 +1276,6 @@ # # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound -# If @size is negative, InvalidParameterValue -# If the block device has no medium inserted, DeviceHasNoMedium -# If the block device does not support resize, Unsupported -# If the block device is read-only, DeviceIsReadOnly -# If a long-running operation is using the device, DeviceInUse # # Since: 0.14.0 ## @@ -1233,10 +1337,6 @@ # # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound -# If @device is busy, DeviceInUse will be returned -# If @snapshot-file can't be created, OpenFileFailed -# If @snapshot-file can't be opened, OpenFileFailed -# If @format is invalid, InvalidBlockFormat # # Note: The transaction aborts on the first failure. Therefore, there will # be only one device or snapshot file returned in an error condition, and @@ -1265,8 +1365,6 @@ # # 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 # # Since 0.14.0 ## @@ -1344,6 +1442,33 @@ { 'command': 'migrate_set_speed', 'data': {'value': 'int'} } ## +# @migrate-set-cache-size +# +# Set XBZRLE cache size +# +# @value: cache size in bytes +# +# The size will be rounded down to the nearest power of 2. +# The cache size can be modified before and during ongoing migration +# +# Returns: nothing on success +# +# Since: 1.2 +## +{ 'command': 'migrate-set-cache-size', 'data': {'value': 'int'} } + +## +# @query-migrate-cache-size +# +# query XBZRLE cache size +# +# Returns: XBZRLE cache size in bytes +# +# Since: 1.2 +## +{ 'command': 'query-migrate-cache-size', 'returns': 'int' } + +## # @ObjectPropertyInfo: # # @name: the name of the property @@ -1363,9 +1488,7 @@ # 4) A link type in the form 'link<subtype>' where subtype is a qdev # device type name. Link properties form the device model graph. # -# Since: 1.1 -# -# Notes: This type is experimental. Its syntax may change in future releases. +# Since: 1.2 ## { 'type': 'ObjectPropertyInfo', 'data': { 'name': 'str', 'type': 'str' } } @@ -1382,10 +1505,7 @@ # Returns: a list of @ObjectPropertyInfo that describe the properties of the # object. # -# Since: 1.1 -# -# Notes: This command is experimental. It's syntax may change in future -# releases. +# Since: 1.2 ## { 'command': 'qom-list', 'data': { 'path': 'str' }, @@ -1421,9 +1541,7 @@ # returns as #str pathnames. All integer property types (u8, u16, etc) # are returned as #int. # -# Since: 1.1 -# -# Notes: This command is experimental and may change syntax in future releases. +# Since: 1.2 ## { 'command': 'qom-get', 'data': { 'path': 'str', 'property': 'str' }, @@ -1442,9 +1560,7 @@ # @value: a value who's type is appropriate for the property type. See @qom-get # for a description of type mapping. # -# Since: 1.1 -# -# Notes: This command is experimental and may change syntax in future releases. +# Since: 1.2 ## { 'command': 'qom-set', 'data': { 'path': 'str', 'property': 'str', 'value': 'visitor' }, @@ -1468,11 +1584,6 @@ # # Returns: Nothing on success # If Spice is not enabled, DeviceNotFound -# If @protocol does not support connected, InvalidParameter -# If @protocol is invalid, InvalidParameter -# If any other error occurs, SetPasswdFailed -# -# Notes: If VNC is not enabled, SetPasswdFailed is returned. # # Since: 0.14.0 ## @@ -1494,8 +1605,6 @@ # # Returns: Nothing on success # If @protocol is `spice' and Spice is not active, DeviceNotFound -# If an error occurs setting password expiration, SetPasswdFailed -# If @protocol is not `spice' or 'vnc', InvalidParameter # # Since: 0.14.0 # @@ -1518,8 +1627,6 @@ # # Returns: Nothing on success # If @device is not a valid block device, DeviceNotFound -# If @device is not removable and @force is false, DeviceNotRemovable -# If @force is false and @device is locked, DeviceLocked # # Notes: Ejecting a device will no media results in success # @@ -1562,7 +1669,6 @@ # # Returns: Nothing on success. # If @device is not a valid block device, DeviceNotFound -# If @format is not a valid block format, InvalidBlockFormat # If the new block device is encrypted, DeviceEncrypted. Note that # if this error is returned, the device has been opened successfully # and an additional call to @block_passwd is required to set the @@ -1598,7 +1704,6 @@ # # Returns: Nothing on success # If @device is not a valid block device, DeviceNotFound -# If the argument combination is invalid, InvalidParameterCombination # # Since: 1.1 ## @@ -1632,11 +1737,7 @@ # @speed: #optional the maximum speed, in bytes per second # # Returns: Nothing on success -# If streaming is already active on this device, DeviceInUse # If @device does not exist, DeviceNotFound -# If image streaming is not supported by this device, NotSupported -# If @base does not exist, BaseNotFound -# If @speed is invalid, InvalidParameter # # Since: 1.1 ## @@ -1658,9 +1759,7 @@ # Defaults to 0. # # Returns: Nothing on success -# If the job type does not support throttling, NotSupported -# If the speed value is invalid, InvalidParameter -# If streaming is not active on this device, DeviceNotActive +# If no background operation is active on this device, DeviceNotActive # # Since: 1.1 ## @@ -1670,9 +1769,9 @@ ## # @block-job-cancel: # -# Stop an active block streaming operation. +# Stop an active background block operation. # -# This command returns immediately after marking the active block streaming +# This command returns immediately after marking the active background block # operation for cancellation. It is an error to call this command if no # operation is in progress. # @@ -1680,17 +1779,15 @@ # BLOCK_JOB_CANCELLED event. Before that happens the job is still visible when # enumerated using query-block-jobs. # -# The image file retains its backing file unless the streaming operation happens -# to complete just as it is being cancelled. -# -# A new block streaming operation can be started at a later time to finish -# copying all data from the backing file. +# For streaming, the image file retains its backing file unless the streaming +# operation happens to complete just as it is being cancelled. A new streaming +# operation can be started at a later time to finish copying all data from the +# backing file. # # @device: the device name # # Returns: Nothing on success -# If streaming is not active on this device, DeviceNotActive -# If cancellation already in progress, DeviceInUse +# If no background operation is active on this device, DeviceNotActive # # Since: 1.1 ## @@ -1722,14 +1819,40 @@ # Returns: a list of @ObjectTypeInfo or an empty list if no results are found # # Since: 1.1 -# -# Notes: This command is experimental and may change syntax in future releases. ## { 'command': 'qom-list-types', 'data': { '*implements': 'str', '*abstract': 'bool' }, 'returns': [ 'ObjectTypeInfo' ] } ## +# @DevicePropertyInfo: +# +# Information about device properties. +# +# @name: the name of the property +# @type: the typename of the property +# +# Since: 1.2 +## +{ 'type': 'DevicePropertyInfo', + 'data': { 'name': 'str', 'type': 'str' } } + +## +# @device-list-properties: +# +# List properties associated with a device. +# +# @typename: the type name of a device +# +# Returns: a list of DevicePropertyInfo describing a devices properties +# +# Since: 1.2 +## +{ 'command': 'device-list-properties', + 'data': { 'typename': 'str'}, + 'returns': [ 'DevicePropertyInfo' ] } + +## # @migrate # # Migrates the current running guest to another Virtual Machine. @@ -1760,8 +1883,6 @@ # format. # # Returns: Nothing on success -# If @filename cannot be opened, OpenFileFailed -# If an I/O error occurs while writing the file, IOError # # Since: 1.1 ## @@ -1776,7 +1897,6 @@ # # Returns: Nothing on success # If @id is not a valid device, DeviceNotFound -# If the device does not support unplug, BusNoHotplug # # Notes: When this command completes, the device may not be removed from the # guest. Hot removal is an operation that requires guest cooperation. @@ -1817,14 +1937,6 @@ # want to dump all guest's memory, please specify the start @begin and @length # # Returns: nothing on success -# If @begin contains an invalid address, InvalidParameter -# If only one of @begin and @length is specified, MissingParameter -# If @protocol stats with "fd:", and the fd cannot be found, FdNotFound -# If @protocol starts with "file:", and the file cannot be -# opened, OpenFileFailed -# If @protocol does not start with "fd:" or "file:", InvalidParameter -# If an I/O error occurs while writing the file, IOError -# If the target does not support this command, Unsupported # # Since: 1.2 ## @@ -1851,10 +1963,6 @@ # # Returns: Nothing on success # If @type is not a valid network backend, DeviceNotFound -# If @id is not a valid identifier, InvalidParameterValue -# if @id already exists, DuplicateId -# If @props contains an invalid parameter for this backend, -# InvalidParameter ## { 'command': 'netdev_add', 'data': {'type': 'str', 'id': 'str', '*props': '**'}, @@ -2174,8 +2282,6 @@ # @fdname: file descriptor name # # Returns: Nothing on success -# If file descriptor was not received, FdNotSupplied -# If @fdname is not valid, InvalidParameterType # # Since: 0.14.0 # @@ -2195,8 +2301,58 @@ # @fdname: file descriptor name # # Returns: Nothing on success -# If @fdname is not found, FdNotFound # # Since: 0.14.0 ## { 'command': 'closefd', 'data': {'fdname': 'str'} } + +## +# @MachineInfo: +# +# Information describing a machine. +# +# @name: the name of the machine +# +# @alias: #optional an alias for the machine name +# +# @default: #optional whether the machine is default +# +# Since: 1.2.0 +## +{ 'type': 'MachineInfo', + 'data': { 'name': 'str', '*alias': 'str', + '*is-default': 'bool' } } + +## +# @query-machines: +# +# Return a list of supported machines +# +# Returns: a list of MachineInfo +# +# Since: 1.2.0 +## +{ 'command': 'query-machines', 'returns': ['MachineInfo'] } + +## +# @CpuDefinitionInfo: +# +# Virtual CPU definition. +# +# @name: the name of the CPU definition +# +# Since: 1.2.0 +## +{ 'type': 'CpuDefinitionInfo', + 'data': { 'name': 'str' } } + +## +# @query-cpu-definitions: +# +# Return a list of supported virtual CPU definitions +# +# Returns: a list of CpuDefInfo +# +# Since: 1.2.0 +## +{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'] } diff --git a/qapi/qmp-core.h b/qapi/qmp-core.h index b0f64ba1ee..00446cff9b 100644 --- a/qapi/qmp-core.h +++ b/qapi/qmp-core.h @@ -49,6 +49,7 @@ void qmp_disable_command(const char *name); void qmp_enable_command(const char *name); bool qmp_command_is_enabled(const char *name); char **qmp_get_command_list(void); +QObject *qmp_build_error_object(Error *errp); #endif diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 122c1a29ba..4085994686 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -14,8 +14,8 @@ #include "qemu-objects.h" #include "qapi/qmp-core.h" #include "json-parser.h" +#include "qapi-types.h" #include "error.h" -#include "error_int.h" #include "qerror.h" static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) @@ -109,6 +109,13 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp) return ret; } +QObject *qmp_build_error_object(Error *errp) +{ + return qobject_from_jsonf("{ 'class': %s, 'desc': %s }", + ErrorClass_lookup[error_get_class(errp)], + error_get_pretty(errp)); +} + QObject *qmp_dispatch(QObject *request) { Error *err = NULL; @@ -119,7 +126,7 @@ QObject *qmp_dispatch(QObject *request) rsp = qdict_new(); if (err) { - qdict_put_obj(rsp, "error", error_get_qobject(err)); + qdict_put_obj(rsp, "error", qmp_build_error_object(err)); error_free(err); } else if (ret) { qdict_put_obj(rsp, "return", ret); diff --git a/qemu-char.c b/qemu-char.c index c2aaaeeb8f..382c71ebcd 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2446,7 +2446,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) if (is_listen) { fd = inet_listen_opts(opts, 0, NULL); } else { - fd = inet_connect_opts(opts, NULL); + fd = inet_connect_opts(opts, NULL, NULL); } } if (fd < 0) { diff --git a/qemu-common.h b/qemu-common.h index f16079f432..095e28d89a 100644 --- a/qemu-common.h +++ b/qemu-common.h @@ -1,3 +1,4 @@ + /* Common header file that is included by all of qemu. */ #ifndef QEMU_COMMON_H #define QEMU_COMMON_H @@ -376,6 +377,7 @@ bool buffer_is_zero(const void *buf, size_t len); void qemu_progress_init(int enabled, float min_skip); void qemu_progress_end(void); void qemu_progress_print(float delta, int max); +const char *qemu_get_vm_name(void); #define QEMU_FILE_TYPE_BIOS 0 #define QEMU_FILE_TYPE_KEYMAP 1 @@ -428,6 +430,26 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) /* Round number up to multiple */ #define QEMU_ALIGN_UP(n, m) QEMU_ALIGN_DOWN((n) + (m) - 1, (m)) +static inline bool is_power_of_2(uint64_t value) +{ + if (!value) { + return 0; + } + + return !(value & (value - 1)); +} + +/* round down to the nearest power of 2*/ +int64_t pow2floor(int64_t value); + #include "module.h" +/* + * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) + * Input is limited to 14-bit numbers + */ + +int uleb128_encode_small(uint8_t *out, uint32_t n); +int uleb128_decode_small(const uint8_t *in, uint32_t *n); + #endif diff --git a/qemu-doc.texi b/qemu-doc.texi index f32e9e2fb9..35cabbcb9e 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -734,6 +734,11 @@ Various session related parameters can be set via special options, either in a configuration file provided via '-readconfig' or directly on the command line. +If the initiator-name is not specified qemu will use a default name +of 'iqn.2008-11.org.linux-kvm[:<name>'] where <name> is the name of the +virtual machine. + + @example Setting a specific initiator name to use when logging in to the target -iscsi initiator-name=iqn.qemu.test:my-initiator diff --git a/qemu-ga.c b/qemu-ga.c index f1a39ec3a6..8f87621ae4 100644 --- a/qemu-ga.c +++ b/qemu-ga.c @@ -28,7 +28,6 @@ #include "module.h" #include "signal.h" #include "qerror.h" -#include "error_int.h" #include "qapi/qmp-core.h" #include "qga/channel.h" #ifdef _WIN32 @@ -515,7 +514,7 @@ static void process_event(JSONMessageParser *parser, QList *tokens) } else { g_warning("failed to parse event: %s", error_get_pretty(err)); } - qdict_put_obj(qdict, "error", error_get_qobject(err)); + qdict_put_obj(qdict, "error", qmp_build_error_object(err)); error_free(err); } else { qdict = qobject_to_qdict(obj); @@ -532,7 +531,7 @@ static void process_event(JSONMessageParser *parser, QList *tokens) qdict = qdict_new(); g_warning("unrecognized payload format"); error_set(&err, QERR_UNSUPPORTED); - qdict_put_obj(qdict, "error", error_get_qobject(err)); + qdict_put_obj(qdict, "error", qmp_build_error_object(err)); error_free(err); } ret = send_response(s, QOBJECT(qdict)); diff --git a/qemu-img.c b/qemu-img.c index b866f8081e..b41e670a61 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -379,7 +379,7 @@ static int img_check(int argc, char **argv) BlockDriverState *bs; BdrvCheckResult result; int fix = 0; - int flags = BDRV_O_FLAGS; + int flags = BDRV_O_FLAGS | BDRV_O_CHECK; fmt = NULL; for(;;) { @@ -1567,14 +1567,19 @@ static int img_resize(int argc, char **argv) const char *filename, *fmt, *size; int64_t n, total_size; BlockDriverState *bs = NULL; - QEMUOptionParameter *param; - QEMUOptionParameter resize_options[] = { - { - .name = BLOCK_OPT_SIZE, - .type = OPT_SIZE, - .help = "Virtual disk size" + QemuOpts *param; + static QemuOptsList resize_options = { + .name = "resize_options", + .head = QTAILQ_HEAD_INITIALIZER(resize_options.head), + .desc = { + { + .name = BLOCK_OPT_SIZE, + .type = QEMU_OPT_SIZE, + .help = "Virtual disk size" + }, { + /* end of list */ + } }, - { NULL } }; /* Remove size from argv manually so that negative numbers are not treated @@ -1624,14 +1629,15 @@ static int img_resize(int argc, char **argv) } /* Parse size */ - param = parse_option_parameters("", resize_options, NULL); - if (set_option_parameter(param, BLOCK_OPT_SIZE, size)) { + param = qemu_opts_create(&resize_options, NULL, 0, NULL); + if (qemu_opt_set(param, BLOCK_OPT_SIZE, size)) { /* Error message already printed when size parsing fails */ ret = -1; + qemu_opts_del(param); goto out; } - n = get_option_parameter(param, BLOCK_OPT_SIZE)->value.n; - free_option_parameters(param); + n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0); + qemu_opts_del(param); bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR); if (!bs) { diff --git a/qemu-io.c b/qemu-io.c index 8f3b94b838..d0f4fb70c7 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -1652,6 +1652,17 @@ static const cmdinfo_t map_cmd = { .oneline = "prints the allocated areas of a file", }; +static int abort_f(int argc, char **argv) +{ + abort(); +} + +static const cmdinfo_t abort_cmd = { + .name = "abort", + .cfunc = abort_f, + .flags = CMD_NOFILE_OK, + .oneline = "simulate a program crash using abort(3)", +}; static int close_f(int argc, char **argv) { @@ -1905,6 +1916,7 @@ int main(int argc, char **argv) add_command(&discard_cmd); add_command(&alloc_cmd); add_command(&map_cmd); + add_command(&abort_cmd); add_args_command(init_args_command); add_check_command(init_check_command); diff --git a/qemu-options.hx b/qemu-options.hx index 5e7d0dc035..47cb5bd311 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1897,6 +1897,11 @@ images for the guest storage. Both disk and cdrom images are supported. Syntax for specifying iSCSI LUNs is ``iscsi://<target-ip>[:<port>]/<target-iqn>/<lun>'' +By default qemu will use the iSCSI initiator-name +'iqn.2008-11.org.linux-kvm[:<name>]' but this can also be set from the command +line or a configuration file. + + Example (without authentication): @example qemu-system-i386 -iscsi initiator-name=iqn.2001-04.com.example:my-initiator \ @@ -1926,6 +1931,9 @@ DEF("iscsi", HAS_ARG, QEMU_OPTION_iscsi, " iSCSI session parameters\n", QEMU_ARCH_ALL) STEXI +iSCSI parameters such as username and password can also be specified via +a configuration file. See qemu-doc for more information and examples. + @item NBD QEMU supports NBD (Network Block Devices) both using TCP protocol as well as Unix Domain Sockets. diff --git a/qemu-sockets.c b/qemu-sockets.c index beb2bb6f4a..361d890da3 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -209,7 +209,7 @@ listen: return slisten; } -int inet_connect_opts(QemuOpts *opts, Error **errp) +int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp) { struct addrinfo ai,*res,*e; const char *addr; @@ -224,6 +224,10 @@ int inet_connect_opts(QemuOpts *opts, Error **errp) ai.ai_family = PF_UNSPEC; ai.ai_socktype = SOCK_STREAM; + if (in_progress) { + *in_progress = false; + } + addr = qemu_opt_get(opts, "host"); port = qemu_opt_get(opts, "port"); block = qemu_opt_get_bool(opts, "block", 0); @@ -277,7 +281,9 @@ int inet_connect_opts(QemuOpts *opts, Error **errp) #else if (!block && (rc == -EINPROGRESS)) { #endif - error_set(errp, QERR_SOCKET_CONNECT_IN_PROGRESS); + if (in_progress) { + *in_progress = true; + } } else if (rc < 0) { if (NULL == e->ai_next) fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__, @@ -487,7 +493,7 @@ int inet_listen(const char *str, char *ostr, int olen, return sock; } -int inet_connect(const char *str, bool block, Error **errp) +int inet_connect(const char *str, bool block, bool *in_progress, Error **errp) { QemuOpts *opts; int sock = -1; @@ -497,7 +503,7 @@ int inet_connect(const char *str, bool block, Error **errp) if (block) { qemu_opt_set(opts, "block", "on"); } - sock = inet_connect_opts(opts, errp); + sock = inet_connect_opts(opts, in_progress, errp); } else { error_set(errp, QERR_SOCKET_CREATE_FAILED); } diff --git a/qemu-timer.c b/qemu-timer.c index 062fdf2cb9..5aea94e8e0 100644 --- a/qemu-timer.c +++ b/qemu-timer.c @@ -112,14 +112,10 @@ static int64_t qemu_next_alarm_deadline(void) static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) { - int64_t nearest_delta_ns; - if (!rt_clock->active_timers && - !vm_clock->active_timers && - !host_clock->active_timers) { - return; + int64_t nearest_delta_ns = qemu_next_alarm_deadline(); + if (nearest_delta_ns < INT64_MAX) { + t->rearm(t, nearest_delta_ns); } - nearest_delta_ns = qemu_next_alarm_deadline(); - t->rearm(t, nearest_delta_ns); } /* TODO: MIN_TIMER_REARM_NS should be optimized */ diff --git a/qemu-tool.c b/qemu-tool.c index 318c5fcbca..64b5e88bc7 100644 --- a/qemu-tool.c +++ b/qemu-tool.c @@ -30,6 +30,11 @@ struct QEMUBH void *opaque; }; +const char *qemu_get_vm_name(void) +{ + return NULL; +} + Monitor *cur_mon; int monitor_cur_is_qmp(void) diff --git a/qemu_socket.h b/qemu_socket.h index 4689ff340d..30ae6af8b8 100644 --- a/qemu_socket.h +++ b/qemu_socket.h @@ -42,8 +42,8 @@ int send_all(int fd, const void *buf, int len1); int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp); int inet_listen(const char *str, char *ostr, int olen, int socktype, int port_offset, Error **errp); -int inet_connect_opts(QemuOpts *opts, Error **errp); -int inet_connect(const char *str, bool block, Error **errp); +int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp); +int inet_connect(const char *str, bool block, bool *in_progress, Error **errp); int inet_dgram_opts(QemuOpts *opts); const char *inet_strfamily(int family); diff --git a/qerror.c b/qerror.c index 92c4eff179..08185047b4 100644 --- a/qerror.c +++ b/qerror.c @@ -23,320 +23,11 @@ static const QType qerror_type = { }; /** - * The 'desc' parameter is a printf-like string, the format of the format - * string is: - * - * %(KEY) - * - * Where KEY is a QDict key, which has to be passed to qerror_from_info(). - * - * Example: - * - * "foo error on device: %(device) slot: %(slot_nr)" - * - * A single percent sign can be printed if followed by a second one, - * for example: - * - * "running out of foo: %(foo)%%" - * - * Please keep the entries in alphabetical order. - * Use scripts/check-qerror.sh to check. - */ -static const QErrorStringTable qerror_table[] = { - { - .error_fmt = QERR_ADD_CLIENT_FAILED, - .desc = "Could not add client", - }, - { - .error_fmt = QERR_AMBIGUOUS_PATH, - .desc = "Path '%(path)' does not uniquely identify a %(object)" - }, - { - .error_fmt = QERR_BAD_BUS_FOR_DEVICE, - .desc = "Device '%(device)' can't go on a %(bad_bus_type) bus", - }, - { - .error_fmt = QERR_BASE_NOT_FOUND, - .desc = "Base '%(base)' not found", - }, - { - .error_fmt = QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, - .desc = "Block format '%(format)' used by device '%(name)' does not support feature '%(feature)'", - }, - { - .error_fmt = QERR_BUS_NO_HOTPLUG, - .desc = "Bus '%(bus)' does not support hotplugging", - }, - { - .error_fmt = QERR_BUS_NOT_FOUND, - .desc = "Bus '%(bus)' not found", - }, - { - .error_fmt = QERR_COMMAND_DISABLED, - .desc = "The command %(name) has been disabled for this instance", - }, - { - .error_fmt = QERR_COMMAND_NOT_FOUND, - .desc = "The command %(name) has not been found", - }, - { - .error_fmt = QERR_DEVICE_ENCRYPTED, - .desc = "Device '%(device)' is encrypted", - }, - { - .error_fmt = QERR_DEVICE_FEATURE_BLOCKS_MIGRATION, - .desc = "Migration is disabled when using feature '%(feature)' in device '%(device)'", - }, - { - .error_fmt = QERR_DEVICE_HAS_NO_MEDIUM, - .desc = "Device '%(device)' has no medium", - }, - { - .error_fmt = QERR_DEVICE_INIT_FAILED, - .desc = "Device '%(device)' could not be initialized", - }, - { - .error_fmt = QERR_DEVICE_IN_USE, - .desc = "Device '%(device)' is in use", - }, - { - .error_fmt = QERR_DEVICE_IS_READ_ONLY, - .desc = "Device '%(device)' is read only", - }, - { - .error_fmt = QERR_DEVICE_LOCKED, - .desc = "Device '%(device)' is locked", - }, - { - .error_fmt = QERR_DEVICE_MULTIPLE_BUSSES, - .desc = "Device '%(device)' has multiple child busses", - }, - { - .error_fmt = QERR_DEVICE_NO_BUS, - .desc = "Device '%(device)' has no child bus", - }, - { - .error_fmt = QERR_DEVICE_NO_HOTPLUG, - .desc = "Device '%(device)' does not support hotplugging", - }, - { - .error_fmt = QERR_DEVICE_NOT_ACTIVE, - .desc = "Device '%(device)' has not been activated", - }, - { - .error_fmt = QERR_DEVICE_NOT_ENCRYPTED, - .desc = "Device '%(device)' is not encrypted", - }, - { - .error_fmt = QERR_DEVICE_NOT_FOUND, - .desc = "Device '%(device)' not found", - }, - { - .error_fmt = QERR_DEVICE_NOT_REMOVABLE, - .desc = "Device '%(device)' is not removable", - }, - { - .error_fmt = QERR_DUPLICATE_ID, - .desc = "Duplicate ID '%(id)' for %(object)", - }, - { - .error_fmt = QERR_FD_NOT_FOUND, - .desc = "File descriptor named '%(name)' not found", - }, - { - .error_fmt = QERR_FD_NOT_SUPPLIED, - .desc = "No file descriptor supplied via SCM_RIGHTS", - }, - { - .error_fmt = QERR_FEATURE_DISABLED, - .desc = "The feature '%(name)' is not enabled", - }, - { - .error_fmt = QERR_INVALID_BLOCK_FORMAT, - .desc = "Invalid block format '%(name)'", - }, - { - .error_fmt = QERR_INVALID_OPTION_GROUP, - .desc = "There is no option group '%(group)'", - }, - { - .error_fmt = QERR_INVALID_PARAMETER, - .desc = "Invalid parameter '%(name)'", - }, - { - .error_fmt = QERR_INVALID_PARAMETER_COMBINATION, - .desc = "Invalid parameter combination", - }, - { - .error_fmt = QERR_INVALID_PARAMETER_TYPE, - .desc = "Invalid parameter type for '%(name)', expected: %(expected)", - }, - { - .error_fmt = QERR_INVALID_PARAMETER_VALUE, - .desc = "Parameter '%(name)' expects %(expected)", - }, - { - .error_fmt = QERR_INVALID_PASSWORD, - .desc = "Password incorrect", - }, - { - .error_fmt = QERR_IO_ERROR, - .desc = "An IO error has occurred", - }, - { - .error_fmt = QERR_JSON_PARSE_ERROR, - .desc = "JSON parse error, %(message)", - - }, - { - .error_fmt = QERR_JSON_PARSING, - .desc = "Invalid JSON syntax", - }, - { - .error_fmt = QERR_KVM_MISSING_CAP, - .desc = "Using KVM without %(capability), %(feature) unavailable", - }, - { - .error_fmt = QERR_MIGRATION_ACTIVE, - .desc = "There's a migration process in progress", - }, - { - .error_fmt = QERR_MIGRATION_NOT_SUPPORTED, - .desc = "State blocked by non-migratable device '%(device)'", - }, - { - .error_fmt = QERR_MIGRATION_EXPECTED, - .desc = "An incoming migration is expected before this command can be executed", - }, - { - .error_fmt = QERR_MISSING_PARAMETER, - .desc = "Parameter '%(name)' is missing", - }, - { - .error_fmt = QERR_NO_BUS_FOR_DEVICE, - .desc = "No '%(bus)' bus found for device '%(device)'", - }, - { - .error_fmt = QERR_NOT_SUPPORTED, - .desc = "Not supported", - }, - { - .error_fmt = QERR_OPEN_FILE_FAILED, - .desc = "Could not open '%(filename)'", - }, - { - .error_fmt = QERR_PERMISSION_DENIED, - .desc = "Insufficient permission to perform this operation", - }, - { - .error_fmt = QERR_PROPERTY_NOT_FOUND, - .desc = "Property '%(device).%(property)' not found", - }, - { - .error_fmt = QERR_PROPERTY_VALUE_BAD, - .desc = "Property '%(device).%(property)' doesn't take value '%(value)'", - }, - { - .error_fmt = QERR_PROPERTY_VALUE_IN_USE, - .desc = "Property '%(device).%(property)' can't take value '%(value)', it's in use", - }, - { - .error_fmt = QERR_PROPERTY_VALUE_NOT_FOUND, - .desc = "Property '%(device).%(property)' can't find value '%(value)'", - }, - { - .error_fmt = QERR_PROPERTY_VALUE_NOT_POWER_OF_2, - .desc = "Property '%(device).%(property)' doesn't take " - "value '%(value)', it's not a power of 2", - }, - { - .error_fmt = QERR_PROPERTY_VALUE_OUT_OF_RANGE, - .desc = "Property '%(device).%(property)' doesn't take " - "value %(value) (minimum: %(min), maximum: %(max))", - }, - { - .error_fmt = QERR_QGA_COMMAND_FAILED, - .desc = "Guest agent command failed, error was '%(message)'", - }, - { - .error_fmt = QERR_QGA_LOGGING_FAILED, - .desc = "Guest agent failed to log non-optional log statement", - }, - { - .error_fmt = QERR_QMP_BAD_INPUT_OBJECT, - .desc = "Expected '%(expected)' in QMP input", - }, - { - .error_fmt = QERR_QMP_BAD_INPUT_OBJECT_MEMBER, - .desc = "QMP input object member '%(member)' expects '%(expected)'", - }, - { - .error_fmt = QERR_QMP_EXTRA_MEMBER, - .desc = "QMP input object member '%(member)' is unexpected", - }, - { - .error_fmt = QERR_RESET_REQUIRED, - .desc = "Resetting the Virtual Machine is required", - }, - { - .error_fmt = QERR_SET_PASSWD_FAILED, - .desc = "Could not set password", - }, - { - .error_fmt = QERR_TOO_MANY_FILES, - .desc = "Too many open files", - }, - { - .error_fmt = QERR_UNDEFINED_ERROR, - .desc = "An undefined error has occurred", - }, - { - .error_fmt = QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - .desc = "'%(device)' uses a %(format) feature which is not " - "supported by this qemu version: %(feature)", - }, - { - .error_fmt = QERR_UNSUPPORTED, - .desc = "this feature or command is not currently supported", - }, - { - .error_fmt = QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION, - .desc = "Migration is disabled when VirtFS export path '%(path)' " - "is mounted in the guest using mount_tag '%(tag)'", - }, - { - .error_fmt = QERR_VNC_SERVER_FAILED, - .desc = "Could not start VNC server on %(target)", - }, - { - .error_fmt = QERR_SOCKET_CONNECT_IN_PROGRESS, - .desc = "Connection can not be completed immediately", - }, - { - .error_fmt = QERR_SOCKET_CONNECT_FAILED, - .desc = "Failed to connect to socket", - }, - { - .error_fmt = QERR_SOCKET_LISTEN_FAILED, - .desc = "Failed to set socket to listening mode", - }, - { - .error_fmt = QERR_SOCKET_BIND_FAILED, - .desc = "Failed to bind socket", - }, - { - .error_fmt = QERR_SOCKET_CREATE_FAILED, - .desc = "Failed to create socket", - }, - {} -}; - -/** * qerror_new(): Create a new QError * * Return strong reference. */ -QError *qerror_new(void) +static QError *qerror_new(void) { QError *qerr; @@ -346,200 +37,31 @@ QError *qerror_new(void) return qerr; } -static void GCC_FMT_ATTR(2, 3) qerror_abort(const QError *qerr, - const char *fmt, ...) -{ - va_list ap; - - fprintf(stderr, "qerror: bad call in function '%s':\n", qerr->func); - fprintf(stderr, "qerror: -> "); - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - fprintf(stderr, "\nqerror: call at %s:%d\n", qerr->file, qerr->linenr); - abort(); -} - -static void GCC_FMT_ATTR(2, 0) qerror_set_data(QError *qerr, - const char *fmt, va_list *va) -{ - QObject *obj; - - obj = qobject_from_jsonv(fmt, va); - if (!obj) { - qerror_abort(qerr, "invalid format '%s'", fmt); - } - if (qobject_type(obj) != QTYPE_QDICT) { - qerror_abort(qerr, "error format is not a QDict '%s'", fmt); - } - - qerr->error = qobject_to_qdict(obj); - - obj = qdict_get(qerr->error, "class"); - if (!obj) { - qerror_abort(qerr, "missing 'class' key in '%s'", fmt); - } - if (qobject_type(obj) != QTYPE_QSTRING) { - qerror_abort(qerr, "'class' key value should be a QString"); - } - - obj = qdict_get(qerr->error, "data"); - if (!obj) { - qerror_abort(qerr, "missing 'data' key in '%s'", fmt); - } - if (qobject_type(obj) != QTYPE_QDICT) { - qerror_abort(qerr, "'data' key value should be a QDICT"); - } -} - -static void qerror_set_desc(QError *qerr, const char *fmt) -{ - int i; - - // FIXME: inefficient loop - - for (i = 0; qerror_table[i].error_fmt; i++) { - if (strcmp(qerror_table[i].error_fmt, fmt) == 0) { - qerr->entry = &qerror_table[i]; - return; - } - } - - qerror_abort(qerr, "error format '%s' not found", fmt); -} - /** * qerror_from_info(): Create a new QError from error information * - * The information consists of: - * - * - file the file name of where the error occurred - * - linenr the line number of where the error occurred - * - func the function name of where the error occurred - * - fmt JSON printf-like dictionary, there must exist keys 'class' and - * 'data' - * - va va_list of all arguments specified by fmt - * * Return strong reference. */ -QError *qerror_from_info(const char *file, int linenr, const char *func, - const char *fmt, va_list *va) +static QError *qerror_from_info(ErrorClass err_class, const char *fmt, + va_list *va) { QError *qerr; qerr = qerror_new(); loc_save(&qerr->loc); - qerr->linenr = linenr; - qerr->file = file; - qerr->func = func; - - if (!fmt) { - qerror_abort(qerr, "QDict not specified"); - } - qerror_set_data(qerr, fmt, va); - qerror_set_desc(qerr, fmt); + qerr->err_msg = g_strdup_vprintf(fmt, *va); + qerr->err_class = err_class; return qerr; } -static void parse_error(const QErrorStringTable *entry, int c) -{ - fprintf(stderr, "expected '%c' in '%s'", c, entry->desc); - abort(); -} - -static const char *append_field(QDict *error, QString *outstr, - const QErrorStringTable *entry, - const char *start) -{ - QObject *obj; - QDict *qdict; - QString *key_qs; - const char *end, *key; - - if (*start != '%') - parse_error(entry, '%'); - start++; - if (*start != '(') - parse_error(entry, '('); - start++; - - end = strchr(start, ')'); - if (!end) - parse_error(entry, ')'); - - key_qs = qstring_from_substr(start, 0, end - start - 1); - key = qstring_get_str(key_qs); - - qdict = qobject_to_qdict(qdict_get(error, "data")); - obj = qdict_get(qdict, key); - if (!obj) { - abort(); - } - - switch (qobject_type(obj)) { - case QTYPE_QSTRING: - qstring_append(outstr, qdict_get_str(qdict, key)); - break; - case QTYPE_QINT: - qstring_append_int(outstr, qdict_get_int(qdict, key)); - break; - default: - abort(); - } - - QDECREF(key_qs); - return ++end; -} - -static QString *qerror_format_desc(QDict *error, - const QErrorStringTable *entry) -{ - QString *qstring; - const char *p; - - assert(entry != NULL); - - qstring = qstring_new(); - - for (p = entry->desc; *p != '\0';) { - if (*p != '%') { - qstring_append_chr(qstring, *p++); - } else if (*(p + 1) == '%') { - qstring_append_chr(qstring, '%'); - p += 2; - } else { - p = append_field(error, qstring, entry, p); - } - } - - return qstring; -} - -QString *qerror_format(const char *fmt, QDict *error) -{ - const QErrorStringTable *entry = NULL; - int i; - - for (i = 0; qerror_table[i].error_fmt; i++) { - if (strcmp(qerror_table[i].error_fmt, fmt) == 0) { - entry = &qerror_table[i]; - break; - } - } - - return qerror_format_desc(error, entry); -} - /** * qerror_human(): Format QError data into human-readable string. */ QString *qerror_human(const QError *qerror) { - return qerror_format_desc(qerror->error, qerror->entry); + return qstring_from_str(qerror->err_msg); } /** @@ -549,7 +71,7 @@ QString *qerror_human(const QError *qerror) * it uses error_report() for this, so that the output is routed to the right * place (ie. stderr or Monitor's device). */ -void qerror_print(QError *qerror) +static void qerror_print(QError *qerror) { QString *qstring = qerror_human(qerror); loc_push_restore(&qerror->loc); @@ -558,14 +80,13 @@ void qerror_print(QError *qerror) QDECREF(qstring); } -void qerror_report_internal(const char *file, int linenr, const char *func, - const char *fmt, ...) +void qerror_report(ErrorClass eclass, const char *fmt, ...) { va_list va; QError *qerror; va_start(va, fmt); - qerror = qerror_from_info(file, linenr, func, fmt, &va); + qerror = qerror_from_info(eclass, fmt, &va); va_end(va); if (monitor_cur_is_qmp()) { @@ -579,27 +100,18 @@ void qerror_report_internal(const char *file, int linenr, const char *func, /* Evil... */ struct Error { - QDict *obj; - const char *fmt; char *msg; + ErrorClass err_class; }; void qerror_report_err(Error *err) { QError *qerr; - int i; qerr = qerror_new(); loc_save(&qerr->loc); - QINCREF(err->obj); - qerr->error = err->obj; - - for (i = 0; qerror_table[i].error_fmt; i++) { - if (strcmp(qerror_table[i].error_fmt, err->fmt) == 0) { - qerr->entry = &qerror_table[i]; - break; - } - } + qerr->err_msg = g_strdup(err->msg); + qerr->err_class = err->err_class; if (monitor_cur_is_qmp()) { monitor_set_error(cur_mon, qerr); @@ -620,7 +132,7 @@ void assert_no_error(Error *err) /** * qobject_to_qerror(): Convert a QObject into a QError */ -QError *qobject_to_qerror(const QObject *obj) +static QError *qobject_to_qerror(const QObject *obj) { if (qobject_type(obj) != QTYPE_QERROR) { return NULL; @@ -639,6 +151,6 @@ static void qerror_destroy_obj(QObject *obj) assert(obj != NULL); qerr = qobject_to_qerror(obj); - QDECREF(qerr->error); + g_free(qerr->err_msg); g_free(qerr); } diff --git a/qerror.h b/qerror.h index b4c8758f40..d0a76a4f71 100644 --- a/qerror.h +++ b/qerror.h @@ -16,36 +16,20 @@ #include "qstring.h" #include "qemu-error.h" #include "error.h" +#include "qapi-types.h" #include <stdarg.h> -typedef struct QErrorStringTable { - const char *desc; - const char *error_fmt; -} QErrorStringTable; - typedef struct QError { QObject_HEAD; - QDict *error; Location loc; - int linenr; - const char *file; - const char *func; - const QErrorStringTable *entry; + char *err_msg; + ErrorClass err_class; } QError; -QError *qerror_new(void); -QError *qerror_from_info(const char *file, int linenr, const char *func, - const char *fmt, va_list *va) GCC_FMT_ATTR(4, 0); QString *qerror_human(const QError *qerror); -void qerror_print(QError *qerror); -void qerror_report_internal(const char *file, int linenr, const char *func, - const char *fmt, ...) GCC_FMT_ATTR(4, 5); +void qerror_report(ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(2, 3); void qerror_report_err(Error *err); void assert_no_error(Error *err); -QString *qerror_format(const char *fmt, QDict *error); -#define qerror_report(fmt, ...) \ - qerror_report_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__) -QError *qobject_to_qerror(const QObject *obj); /* * QError class list @@ -53,217 +37,213 @@ QError *qobject_to_qerror(const QObject *obj); * Use scripts/check-qerror.sh to check. */ #define QERR_ADD_CLIENT_FAILED \ - "{ 'class': 'AddClientFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Could not add client" #define QERR_AMBIGUOUS_PATH \ - "{ 'class': 'AmbiguousPath', 'data': { 'path': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Path '%s' does not uniquely identify an object" #define QERR_BAD_BUS_FOR_DEVICE \ - "{ 'class': 'BadBusForDevice', 'data': { 'device': %s, 'bad_bus_type': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' can't go on a %s bus" #define QERR_BASE_NOT_FOUND \ - "{ 'class': 'BaseNotFound', 'data': { 'base': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Base '%s' not found" #define QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED \ - "{ 'class': 'BlockFormatFeatureNotSupported', 'data': { 'format': %s, 'name': %s, 'feature': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by device '%s' does not support feature '%s'" #define QERR_BUFFER_OVERRUN \ - "{ 'class': 'BufferOverrun', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "An internal buffer overran" #define QERR_BUS_NO_HOTPLUG \ - "{ 'class': 'BusNoHotplug', 'data': { 'bus': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Bus '%s' does not support hotplugging" #define QERR_BUS_NOT_FOUND \ - "{ 'class': 'BusNotFound', 'data': { 'bus': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Bus '%s' not found" #define QERR_COMMAND_DISABLED \ - "{ 'class': 'CommandDisabled', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "The command %s has been disabled for this instance" #define QERR_COMMAND_NOT_FOUND \ - "{ 'class': 'CommandNotFound', 'data': { 'name': %s } }" + ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has not been found" #define QERR_DEVICE_ENCRYPTED \ - "{ 'class': 'DeviceEncrypted', 'data': { 'device': %s, 'filename': %s } }" + ERROR_CLASS_DEVICE_ENCRYPTED, "'%s' (%s) is encrypted" #define QERR_DEVICE_FEATURE_BLOCKS_MIGRATION \ - "{ 'class': 'DeviceFeatureBlocksMigration', 'data': { 'device': %s, 'feature': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when using feature '%s' in device '%s'" #define QERR_DEVICE_HAS_NO_MEDIUM \ - "{ 'class': 'DeviceHasNoMedium', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' has no medium" #define QERR_DEVICE_INIT_FAILED \ - "{ 'class': 'DeviceInitFailed', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' could not be initialized" #define QERR_DEVICE_IN_USE \ - "{ 'class': 'DeviceInUse', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' is in use" #define QERR_DEVICE_IS_READ_ONLY \ - "{ 'class': 'DeviceIsReadOnly', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' is read only" #define QERR_DEVICE_LOCKED \ - "{ 'class': 'DeviceLocked', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' is locked" #define QERR_DEVICE_MULTIPLE_BUSSES \ - "{ 'class': 'DeviceMultipleBusses', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' has multiple child busses" #define QERR_DEVICE_NO_BUS \ - "{ 'class': 'DeviceNoBus', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' has no child bus" #define QERR_DEVICE_NO_HOTPLUG \ - "{ 'class': 'DeviceNoHotplug', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' does not support hotplugging" #define QERR_DEVICE_NOT_ACTIVE \ - "{ 'class': 'DeviceNotActive', 'data': { 'device': %s } }" + ERROR_CLASS_DEVICE_NOT_ACTIVE, "Device '%s' has not been activated" #define QERR_DEVICE_NOT_ENCRYPTED \ - "{ 'class': 'DeviceNotEncrypted', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' is not encrypted" #define QERR_DEVICE_NOT_FOUND \ - "{ 'class': 'DeviceNotFound', 'data': { 'device': %s } }" + ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found" #define QERR_DEVICE_NOT_REMOVABLE \ - "{ 'class': 'DeviceNotRemovable', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' is not removable" #define QERR_DUPLICATE_ID \ - "{ 'class': 'DuplicateId', 'data': { 'id': %s, 'object': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Duplicate ID '%s' for %s" #define QERR_FD_NOT_FOUND \ - "{ 'class': 'FdNotFound', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "File descriptor named '%s' not found" #define QERR_FD_NOT_SUPPLIED \ - "{ 'class': 'FdNotSupplied', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "No file descriptor supplied via SCM_RIGHTS" #define QERR_FEATURE_DISABLED \ - "{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "The feature '%s' is not enabled" #define QERR_INVALID_BLOCK_FORMAT \ - "{ 'class': 'InvalidBlockFormat', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Invalid block format '%s'" #define QERR_INVALID_OPTION_GROUP \ - "{ 'class': 'InvalidOptionGroup', 'data': { 'group': %s } }" + ERROR_CLASS_GENERIC_ERROR, "There is no option group '%s'" #define QERR_INVALID_PARAMETER \ - "{ 'class': 'InvalidParameter', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Invalid parameter '%s'" #define QERR_INVALID_PARAMETER_COMBINATION \ - "{ 'class': 'InvalidParameterCombination', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Invalid parameter combination" #define QERR_INVALID_PARAMETER_TYPE \ - "{ 'class': 'InvalidParameterType', 'data': { 'name': %s,'expected': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Invalid parameter type for '%s', expected: %s" #define QERR_INVALID_PARAMETER_VALUE \ - "{ 'class': 'InvalidParameterValue', 'data': { 'name': %s, 'expected': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Parameter '%s' expects %s" #define QERR_INVALID_PASSWORD \ - "{ 'class': 'InvalidPassword', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Password incorrect" #define QERR_IO_ERROR \ - "{ 'class': 'IOError', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "An IO error has occurred" #define QERR_JSON_PARSE_ERROR \ - "{ 'class': 'JSONParseError', 'data': { 'message': %s } }" + ERROR_CLASS_GENERIC_ERROR, "JSON parse error, %s" #define QERR_JSON_PARSING \ - "{ 'class': 'JSONParsing', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Invalid JSON syntax" #define QERR_KVM_MISSING_CAP \ - "{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }" + ERROR_CLASS_K_V_M_MISSING_CAP, "Using KVM without %s, %s unavailable" #define QERR_MIGRATION_ACTIVE \ - "{ 'class': 'MigrationActive', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "There's a migration process in progress" #define QERR_MIGRATION_NOT_SUPPORTED \ - "{ 'class': 'MigrationNotSupported', 'data': {'device': %s} }" + ERROR_CLASS_GENERIC_ERROR, "State blocked by non-migratable device '%s'" #define QERR_MIGRATION_EXPECTED \ - "{ 'class': 'MigrationExpected', 'data': {} }" + ERROR_CLASS_MIGRATION_EXPECTED, "An incoming migration is expected before this command can be executed" #define QERR_MISSING_PARAMETER \ - "{ 'class': 'MissingParameter', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Parameter '%s' is missing" #define QERR_NO_BUS_FOR_DEVICE \ - "{ 'class': 'NoBusForDevice', 'data': { 'device': %s, 'bus': %s } }" + ERROR_CLASS_GENERIC_ERROR, "No '%s' bus found for device '%s'" #define QERR_NOT_SUPPORTED \ - "{ 'class': 'NotSupported', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Not supported" #define QERR_OPEN_FILE_FAILED \ - "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Could not open '%s'" #define QERR_PERMISSION_DENIED \ - "{ 'class': 'PermissionDenied', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Insufficient permission to perform this operation" #define QERR_PROPERTY_NOT_FOUND \ - "{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Property '%s.%s' not found" #define QERR_PROPERTY_VALUE_BAD \ - "{ 'class': 'PropertyValueBad', 'data': { 'device': %s, 'property': %s, 'value': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Property '%s.%s' doesn't take value '%s'" #define QERR_PROPERTY_VALUE_IN_USE \ - "{ 'class': 'PropertyValueInUse', 'data': { 'device': %s, 'property': %s, 'value': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Property '%s.%s' can't take value '%s', it's in use" #define QERR_PROPERTY_VALUE_NOT_FOUND \ - "{ 'class': 'PropertyValueNotFound', 'data': { 'device': %s, 'property': %s, 'value': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Property '%s.%s' can't find value '%s'" #define QERR_PROPERTY_VALUE_NOT_POWER_OF_2 \ - "{ 'class': 'PropertyValueNotPowerOf2', 'data': { " \ - "'device': %s, 'property': %s, 'value': %"PRId64" } }" + ERROR_CLASS_GENERIC_ERROR, "Property %s.%s doesn't take value '%" PRId64 "', it's not a power of 2" #define QERR_PROPERTY_VALUE_OUT_OF_RANGE \ - "{ 'class': 'PropertyValueOutOfRange', 'data': { 'device': %s, 'property': %s, 'value': %"PRId64", 'min': %"PRId64", 'max': %"PRId64" } }" + ERROR_CLASS_GENERIC_ERROR, "Property %s.%s doesn't take value %" PRId64 " (minimum: %" PRId64 ", maximum: %" PRId64 ")" #define QERR_QGA_COMMAND_FAILED \ - "{ 'class': 'QgaCommandFailed', 'data': { 'message': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Guest agent command failed, error was '%s'" #define QERR_QGA_LOGGING_FAILED \ - "{ 'class': 'QgaLoggingFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Guest agent failed to log non-optional log statement" #define QERR_QMP_BAD_INPUT_OBJECT \ - "{ 'class': 'QMPBadInputObject', 'data': { 'expected': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Expected '%s' in QMP input" #define QERR_QMP_BAD_INPUT_OBJECT_MEMBER \ - "{ 'class': 'QMPBadInputObjectMember', 'data': { 'member': %s, 'expected': %s } }" + ERROR_CLASS_GENERIC_ERROR, "QMP input object member '%s' expects '%s'" #define QERR_QMP_EXTRA_MEMBER \ - "{ 'class': 'QMPExtraInputObjectMember', 'data': { 'member': %s } }" + ERROR_CLASS_GENERIC_ERROR, "QMP input object member '%s' is unexpected" #define QERR_RESET_REQUIRED \ - "{ 'class': 'ResetRequired', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Resetting the Virtual Machine is required" #define QERR_SET_PASSWD_FAILED \ - "{ 'class': 'SetPasswdFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Could not set password" #define QERR_TOO_MANY_FILES \ - "{ 'class': 'TooManyFiles', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Too many open files" #define QERR_UNDEFINED_ERROR \ - "{ 'class': 'UndefinedError', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "An undefined error has occurred" #define QERR_UNKNOWN_BLOCK_FORMAT_FEATURE \ - "{ 'class': 'UnknownBlockFormatFeature', 'data': { 'device': %s, 'format': %s, 'feature': %s } }" + ERROR_CLASS_GENERIC_ERROR, "'%s' uses a %s feature which is not supported by this qemu version: %s" #define QERR_UNSUPPORTED \ - "{ 'class': 'Unsupported', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "this feature or command is not currently supported" #define QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION \ - "{ 'class': 'VirtFSFeatureBlocksMigration', 'data': { 'path': %s, 'tag': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'" #define QERR_VNC_SERVER_FAILED \ - "{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }" - -#define QERR_SOCKET_CONNECT_IN_PROGRESS \ - "{ 'class': 'SockConnectInprogress', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Could not start VNC server on %s" #define QERR_SOCKET_CONNECT_FAILED \ - "{ 'class': 'SockConnectFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Failed to connect to socket" #define QERR_SOCKET_LISTEN_FAILED \ - "{ 'class': 'SockListenFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Failed to set socket to listening mode" #define QERR_SOCKET_BIND_FAILED \ - "{ 'class': 'SockBindFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Failed to bind socket" #define QERR_SOCKET_CREATE_FAILED \ - "{ 'class': 'SockCreateFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Failed to create socket" #endif /* QERROR_H */ diff --git a/qmp-commands.hx b/qmp-commands.hx index ac466382c0..527b9f7c24 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -435,8 +435,8 @@ Example: -> { "execute": "inject-nmi" } <- { "return": {} } -Note: inject-nmi is only supported for x86 guest currently, it will - returns "Unsupported" error for non-x86 guest. +Note: inject-nmi fails when the guest doesn't support injecting. + Currently, only x86 guests do. EQMP @@ -520,6 +520,50 @@ Example: <- { "return": {} } EQMP +{ + .name = "migrate-set-cache-size", + .args_type = "value:o", + .mhandler.cmd_new = qmp_marshal_input_migrate_set_cache_size, + }, + +SQMP +migrate-set-cache-size +--------------------- + +Set cache size to be used by XBZRLE migration, the cache size will be rounded +down to the nearest power of 2 + +Arguments: + +- "value": cache size in bytes (json-int) + +Example: + +-> { "execute": "migrate-set-cache-size", "arguments": { "value": 536870912 } } +<- { "return": {} } + +EQMP + { + .name = "query-migrate-cache-size", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_migrate_cache_size, + }, + +SQMP +query-migrate-cache-size +--------------------- + +Show cache size to be used by XBZRLE migration + +returns a json-object with the following information: +- "size" : json-int + +Example: + +-> { "execute": "query-migrate-cache-size" } +<- { "return": 67108864 } + +EQMP { .name = "migrate_set_speed", @@ -2078,12 +2122,24 @@ The main json-object contains the following: - "transferred": amount transferred (json-int) - "remaining": amount remaining (json-int) - "total": total (json-int) + - "total-time": total amount of ms since migration started. If + migration has ended, it returns the total migration time + (json-int) + - "duplicate": number of duplicated pages (json-int) + - "normal" : number of normal pages transferred (json-int) + - "normal-bytes" : number of normal bytes transferred (json-int) - "disk": only present if "status" is "active" and it is a block migration, it is a json-object with the following disk information (in bytes): - "transferred": amount transferred (json-int) - "remaining": amount remaining (json-int) - "total": total (json-int) - +- "xbzrle-cache": only present if XBZRLE is active. + It is a json-object with the following XBZRLE information: + - "cache-size": XBZRLE cache size + - "bytes": total XBZRLE bytes transferred + - "pages": number of XBZRLE compressed pages + - "cache-miss": number of cache misses + - "overflow": number of XBZRLE overflows Examples: 1. Before the first migration @@ -2094,7 +2150,19 @@ Examples: 2. Migration is done and has succeeded -> { "execute": "query-migrate" } -<- { "return": { "status": "completed" } } +<- { "return": { + "status": "completed", + "ram":{ + "transferred":123, + "remaining":123, + "total":246, + "total-time":12345, + "duplicate":123, + "normal":123, + "normal-bytes":123456 + } + } + } 3. Migration is done and has failed @@ -2110,7 +2178,11 @@ Examples: "ram":{ "transferred":123, "remaining":123, - "total":246 + "total":246, + "total-time":12345, + "duplicate":123, + "normal":123, + "normal-bytes":123456 } } } @@ -2124,7 +2196,11 @@ Examples: "ram":{ "total":1057024, "remaining":1053304, - "transferred":3720 + "transferred":3720, + "total-time":12345, + "duplicate":123, + "normal":123, + "normal-bytes":123456 }, "disk":{ "total":20971520, @@ -2134,6 +2210,32 @@ Examples: } } +6. Migration is being performed and XBZRLE is active: + +-> { "execute": "query-migrate" } +<- { + "return":{ + "status":"active", + "capabilities" : [ { "capability": "xbzrle", "state" : true } ], + "ram":{ + "total":1057024, + "remaining":1053304, + "transferred":3720, + "total-time":12345, + "duplicate":10, + "normal":3333, + "normal-bytes":3412992 + }, + "xbzrle-cache":{ + "cache-size":67108864, + "bytes":20971520, + "pages":2444343, + "cache-miss":2244, + "overflow":34434 + } + } + } + EQMP { @@ -2143,6 +2245,55 @@ EQMP }, SQMP +migrate-set-capabilities +------- + +Enable/Disable migration capabilities + +- "xbzrle": xbzrle support + +Arguments: + +Example: + +-> { "execute": "migrate-set-capabilities" , "arguments": + { "capabilities": [ { "capability": "xbzrle", "state": true } ] } } + +EQMP + + { + .name = "migrate-set-capabilities", + .args_type = "capabilities:O", + .params = "capability:s,state:b", + .mhandler.cmd_new = qmp_marshal_input_migrate_set_capabilities, + }, +SQMP +query-migrate-capabilities +------- + +Query current migration capabilities + +- "capabilities": migration capabilities state + - "xbzrle" : XBZRLE state (json-bool) + +Arguments: + +Example: + +-> { "execute": "query-migrate-capabilities" } +<- { "return": { + "capabilities" : [ { "capability" : "xbzrle", "state" : false } ] + } + } +EQMP + + { + .name = "query-migrate-capabilities", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_migrate_capabilities, + }, + +SQMP query-balloon ------------- @@ -2217,3 +2368,22 @@ EQMP .args_type = "implements:s?,abstract:b?", .mhandler.cmd_new = qmp_marshal_input_qom_list_types, }, + + { + .name = "device-list-properties", + .args_type = "typename:s", + .mhandler.cmd_new = qmp_marshal_input_device_list_properties, + }, + + { + .name = "query-machines", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_machines, + }, + + { + .name = "query-cpu-definitions", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_cpu_definitions, + }, + diff --git a/qmp.c b/qmp.c index fee9fb2a9d..6c1e4e8978 100644 --- a/qmp.c +++ b/qmp.c @@ -417,3 +417,59 @@ ObjectTypeInfoList *qmp_qom_list_types(bool has_implements, return ret; } + +DevicePropertyInfoList *qmp_device_list_properties(const char *typename, + Error **errp) +{ + ObjectClass *klass; + Property *prop; + DevicePropertyInfoList *prop_list = NULL; + + klass = object_class_by_name(typename); + if (klass == NULL) { + error_set(errp, QERR_DEVICE_NOT_FOUND, typename); + return NULL; + } + + klass = object_class_dynamic_cast(klass, TYPE_DEVICE); + if (klass == NULL) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, + "name", TYPE_DEVICE); + return NULL; + } + + do { + for (prop = DEVICE_CLASS(klass)->props; prop && prop->name; prop++) { + DevicePropertyInfoList *entry; + DevicePropertyInfo *info; + + /* + * TODO Properties without a parser are just for dirty hacks. + * qdev_prop_ptr is the only such PropertyInfo. It's marked + * for removal. This conditional should be removed along with + * it. + */ + if (!prop->info->set) { + continue; /* no way to set it, don't show */ + } + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(prop->name); + info->type = g_strdup(prop->info->legacy_name ?: prop->info->name); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = prop_list; + prop_list = entry; + } + klass = object_class_get_parent(klass); + } while (klass != object_class_by_name(TYPE_DEVICE)); + + return prop_list; +} + +CpuDefinitionInfoList GCC_WEAK *qmp_query_cpu_definitions(Error **errp) +{ + error_set(errp, QERR_NOT_SUPPORTED); + return NULL; +} diff --git a/qom/object.c b/qom/object.c index 00bb3b029c..a552be258e 100644 --- a/qom/object.c +++ b/qom/object.c @@ -31,9 +31,7 @@ typedef struct TypeImpl TypeImpl; struct InterfaceImpl { - const char *parent; - void (*interface_initfn)(ObjectClass *class, void *data); - TypeImpl *type; + const char *typename; }; struct TypeImpl @@ -64,14 +62,6 @@ struct TypeImpl InterfaceImpl interfaces[MAX_INTERFACES]; }; -typedef struct Interface -{ - Object parent; - Object *obj; -} Interface; - -#define INTERFACE(obj) OBJECT_CHECK(Interface, obj, TYPE_INTERFACE) - static Type type_interface; static GHashTable *type_table_get(void) @@ -98,6 +88,7 @@ static TypeImpl *type_table_lookup(const char *name) static TypeImpl *type_register_internal(const TypeInfo *info) { TypeImpl *ti = g_malloc0(sizeof(*ti)); + int i; g_assert(info->name != NULL); @@ -122,15 +113,10 @@ static TypeImpl *type_register_internal(const TypeInfo *info) ti->abstract = info->abstract; - if (info->interfaces) { - int i; - - for (i = 0; info->interfaces[i].type; i++) { - ti->interfaces[i].parent = info->interfaces[i].type; - ti->interfaces[i].interface_initfn = info->interfaces[i].interface_initfn; - ti->num_interfaces++; - } + for (i = 0; info->interfaces && info->interfaces[i].type; i++) { + ti->interfaces[i].typename = g_strdup(info->interfaces[i].type); } + ti->num_interfaces = i; type_table_add(ti); @@ -198,26 +184,48 @@ static size_t type_object_get_size(TypeImpl *ti) return 0; } -static void type_class_interface_init(TypeImpl *ti, InterfaceImpl *iface) +static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type) { - TypeInfo info = { - .instance_size = sizeof(Interface), - .parent = iface->parent, - .class_size = sizeof(InterfaceClass), - .class_init = iface->interface_initfn, - .abstract = true, - }; - char *name = g_strdup_printf("<%s::%s>", ti->name, iface->parent); + assert(target_type); + + /* Check if typename is a direct ancestor of type */ + while (type) { + if (type == target_type) { + return true; + } - info.name = name; - iface->type = type_register_internal(&info); - g_free(name); + type = type_get_parent(type); + } + + return false; +} + +static void type_initialize(TypeImpl *ti); + +static void type_initialize_interface(TypeImpl *ti, const char *parent) +{ + InterfaceClass *new_iface; + TypeInfo info = { }; + TypeImpl *iface_impl; + + info.parent = parent; + info.name = g_strdup_printf("%s::%s", ti->name, info.parent); + info.abstract = true; + + iface_impl = type_register(&info); + type_initialize(iface_impl); + g_free((char *)info.name); + + new_iface = (InterfaceClass *)iface_impl->class; + new_iface->concrete_class = ti->class; + + ti->class->interfaces = g_slist_append(ti->class->interfaces, + iface_impl->class); } static void type_initialize(TypeImpl *ti) { TypeImpl *parent; - int i; if (ti->class) { return; @@ -231,9 +239,33 @@ static void type_initialize(TypeImpl *ti) parent = type_get_parent(ti); if (parent) { type_initialize(parent); + GSList *e; + int i; g_assert(parent->class_size <= ti->class_size); memcpy(ti->class, parent->class, parent->class_size); + + for (e = parent->class->interfaces; e; e = e->next) { + ObjectClass *iface = e->data; + type_initialize_interface(ti, object_class_get_name(iface)); + } + + for (i = 0; i < ti->num_interfaces; i++) { + TypeImpl *t = type_get_by_name(ti->interfaces[i].typename); + for (e = ti->class->interfaces; e; e = e->next) { + TypeImpl *target_type = OBJECT_CLASS(e->data)->type; + + if (type_is_ancestor(target_type, t)) { + break; + } + } + + if (e) { + continue; + } + + type_initialize_interface(ti, ti->interfaces[i].typename); + } } ti->class->type = ti; @@ -245,38 +277,19 @@ static void type_initialize(TypeImpl *ti) parent = type_get_parent(parent); } - for (i = 0; i < ti->num_interfaces; i++) { - type_class_interface_init(ti, &ti->interfaces[i]); - } - if (ti->class_init) { ti->class_init(ti->class, ti->class_data); } -} -static void object_interface_init(Object *obj, InterfaceImpl *iface) -{ - TypeImpl *ti = iface->type; - Interface *iface_obj; - - iface_obj = INTERFACE(object_new(ti->name)); - iface_obj->obj = obj; - obj->interfaces = g_slist_prepend(obj->interfaces, iface_obj); } static void object_init_with_type(Object *obj, TypeImpl *ti) { - int i; - if (type_has_parent(ti)) { object_init_with_type(obj, type_get_parent(ti)); } - for (i = 0; i < ti->num_interfaces; i++) { - object_interface_init(obj, &ti->interfaces[i]); - } - if (ti->instance_init) { ti->instance_init(obj); } @@ -357,12 +370,6 @@ static void object_deinit(Object *obj, TypeImpl *type) type->instance_finalize(obj); } - while (obj->interfaces) { - Interface *iface_obj = obj->interfaces->data; - obj->interfaces = g_slist_delete_link(obj->interfaces, obj->interfaces); - object_delete(OBJECT(iface_obj)); - } - if (type_has_parent(type)) { object_deinit(obj, type_get_parent(type)); } @@ -409,74 +416,15 @@ void object_delete(Object *obj) g_free(obj); } -static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type) -{ - assert(target_type); - - /* Check if typename is a direct ancestor of type */ - while (type) { - if (type == target_type) { - return true; - } - - type = type_get_parent(type); - } - - return false; -} - -static bool object_is_type(Object *obj, TypeImpl *target_type) -{ - return !target_type || type_is_ancestor(obj->class->type, target_type); -} - Object *object_dynamic_cast(Object *obj, const char *typename) { - TypeImpl *target_type = type_get_by_name(typename); - GSList *i; - - /* Check if typename is a direct ancestor. Special-case TYPE_OBJECT, - * we want to go back from interfaces to the parent. - */ - if (target_type && object_is_type(obj, target_type)) { + if (object_class_dynamic_cast(object_get_class(obj), typename)) { return obj; } - /* Check if obj is an interface and its containing object is a direct - * ancestor of typename. In principle we could do this test at the very - * beginning of object_dynamic_cast, avoiding a second call to - * object_is_type. However, casting between interfaces is relatively - * rare, and object_is_type(obj, type_interface) would fail almost always. - * - * Perhaps we could add a magic value to the object header for increased - * (run-time) type safety and to speed up tests like this one. If we ever - * do that we can revisit the order here. - */ - if (object_is_type(obj, type_interface)) { - assert(!obj->interfaces); - obj = INTERFACE(obj)->obj; - if (object_is_type(obj, target_type)) { - return obj; - } - } - - if (!target_type) { - return obj; - } - - /* Check if obj has an interface of typename */ - for (i = obj->interfaces; i; i = i->next) { - Interface *iface = i->data; - - if (object_is_type(OBJECT(iface), target_type)) { - return OBJECT(iface); - } - } - return NULL; } - Object *object_dynamic_cast_assert(Object *obj, const char *typename) { Object *inst; @@ -497,16 +445,30 @@ ObjectClass *object_class_dynamic_cast(ObjectClass *class, { TypeImpl *target_type = type_get_by_name(typename); TypeImpl *type = class->type; + ObjectClass *ret = NULL; - while (type) { - if (type == target_type) { - return class; - } + if (type->num_interfaces && type_is_ancestor(target_type, type_interface)) { + int found = 0; + GSList *i; - type = type_get_parent(type); + for (i = class->interfaces; i; i = i->next) { + ObjectClass *target_class = i->data; + + if (type_is_ancestor(target_class->type, target_type)) { + ret = target_class; + found++; + } + } + + /* The match was ambiguous, don't allow a cast */ + if (found > 1) { + ret = NULL; + } + } else if (type_is_ancestor(type, target_type)) { + ret = class; } - return NULL; + return ret; } ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class, @@ -920,12 +882,6 @@ void object_property_add_child(Object *obj, const char *name, { gchar *type; - /* Registering an interface object in the composition tree will mightily - * confuse object_get_canonical_path (which, on the other hand, knows how - * to get the canonical path of an interface object). - */ - assert(!object_is_type(obj, type_interface)); - type = g_strdup_printf("child<%s>", object_get_typename(OBJECT(child))); object_property_add(obj, name, type, object_get_child_property, @@ -1022,10 +978,6 @@ gchar *object_get_canonical_path(Object *obj) Object *root = object_get_root(); char *newpath = NULL, *path = NULL; - if (object_is_type(obj, type_interface)) { - obj = INTERFACE(obj)->obj; - } - while (obj != root) { ObjectProperty *prop = NULL; @@ -1246,7 +1198,7 @@ static void register_types(void) { static TypeInfo interface_info = { .name = TYPE_INTERFACE, - .instance_size = sizeof(Interface), + .class_size = sizeof(InterfaceClass), .abstract = true, }; diff --git a/savevm.c b/savevm.c index 6e82b2d3e3..0ea10c9b66 100644 --- a/savevm.c +++ b/savevm.c @@ -2392,3 +2392,162 @@ void vmstate_register_ram_global(MemoryRegion *mr) { vmstate_register_ram(mr, NULL); } + +/* + page = zrun nzrun + | zrun nzrun page + + zrun = length + + nzrun = length byte... + + length = uleb128 encoded integer + */ +int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen, + uint8_t *dst, int dlen) +{ + uint32_t zrun_len = 0, nzrun_len = 0; + int d = 0, i = 0; + long res, xor; + uint8_t *nzrun_start = NULL; + + g_assert(!(((uintptr_t)old_buf | (uintptr_t)new_buf | slen) % + sizeof(long))); + + while (i < slen) { + /* overflow */ + if (d + 2 > dlen) { + return -1; + } + + /* not aligned to sizeof(long) */ + res = (slen - i) % sizeof(long); + while (res && old_buf[i] == new_buf[i]) { + zrun_len++; + i++; + res--; + } + + /* word at a time for speed */ + if (!res) { + while (i < slen && + (*(long *)(old_buf + i)) == (*(long *)(new_buf + i))) { + i += sizeof(long); + zrun_len += sizeof(long); + } + + /* go over the rest */ + while (i < slen && old_buf[i] == new_buf[i]) { + zrun_len++; + i++; + } + } + + /* buffer unchanged */ + if (zrun_len == slen) { + return 0; + } + + /* skip last zero run */ + if (i == slen) { + return d; + } + + d += uleb128_encode_small(dst + d, zrun_len); + + zrun_len = 0; + nzrun_start = new_buf + i; + + /* overflow */ + if (d + 2 > dlen) { + return -1; + } + /* not aligned to sizeof(long) */ + res = (slen - i) % sizeof(long); + while (res && old_buf[i] != new_buf[i]) { + i++; + nzrun_len++; + res--; + } + + /* word at a time for speed, use of 32-bit long okay */ + if (!res) { + /* truncation to 32-bit long okay */ + long mask = 0x0101010101010101ULL; + while (i < slen) { + xor = *(long *)(old_buf + i) ^ *(long *)(new_buf + i); + if ((xor - mask) & ~xor & (mask << 7)) { + /* found the end of an nzrun within the current long */ + while (old_buf[i] != new_buf[i]) { + nzrun_len++; + i++; + } + break; + } else { + i += sizeof(long); + nzrun_len += sizeof(long); + } + } + } + + d += uleb128_encode_small(dst + d, nzrun_len); + /* overflow */ + if (d + nzrun_len > dlen) { + return -1; + } + memcpy(dst + d, nzrun_start, nzrun_len); + d += nzrun_len; + nzrun_len = 0; + } + + return d; +} + +int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen) +{ + int i = 0, d = 0; + int ret; + uint32_t count = 0; + + while (i < slen) { + + /* zrun */ + if ((slen - i) < 2) { + return -1; + } + + ret = uleb128_decode_small(src + i, &count); + if (ret < 0 || (i && !count)) { + return -1; + } + i += ret; + d += count; + + /* overflow */ + if (d > dlen) { + return -1; + } + + /* nzrun */ + if ((slen - i) < 2) { + return -1; + } + + ret = uleb128_decode_small(src + i, &count); + if (ret < 0 || !count) { + return -1; + } + i += ret; + + /* overflow */ + if (d + count > dlen || i + count > slen) { + return -1; + } + + memcpy(dst + d, src + i, count); + d += count; + i += count; + } + + return d; +} diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 9eed40e18a..3c4678dbf1 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -342,6 +342,7 @@ def gen_command_decl_prologue(header, guard, prefix=""): #define %(guard)s #include "%(prefix)sqapi-types.h" +#include "qdict.h" #include "error.h" ''', diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 4a734f58d5..cf601ae2d2 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -70,7 +70,7 @@ const char *%(name)s_lookup[] = { ret += mcgen(''' "%(value)s", ''', - value=value.lower()) + value=value) ret += mcgen(''' NULL, @@ -79,6 +79,16 @@ const char *%(name)s_lookup[] = { ''') return ret +def generate_enum_name(name): + if name.isupper(): + return c_fun(name) + new_name = '' + for c in c_fun(name): + if c.isupper(): + new_name += '_' + new_name += c + return new_name.lstrip('_').upper() + def generate_enum(name, values): lookup_decl = mcgen(''' extern const char *%(name)s_lookup[]; @@ -100,7 +110,7 @@ typedef enum %(name)s %(abbrev)s_%(value)s = %(i)d, ''', abbrev=de_camel_case(name).upper(), - value=c_fun(value).upper(), + value=generate_enum_name(value), i=i) i += 1 @@ -253,7 +263,8 @@ fdecl.write(mcgen(''' #ifndef %(guard)s #define %(guard)s -#include "qapi/qapi-types-core.h" +#include "qemu-common.h" + ''', guard=guardname(h_file))) diff --git a/scripts/qapi.py b/scripts/qapi.py index d3b8b4d851..122b4cb6d1 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -142,6 +142,22 @@ def camel_case(name): return new_name def c_var(name): + # ANSI X3J11/88-090, 3.1.1 + c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', + 'default', 'do', 'double', 'else', 'enum', 'extern', 'float', + 'for', 'goto', 'if', 'int', 'long', 'register', 'return', + 'short', 'signed', 'sizeof', 'static', 'struct', 'switch', + 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while']) + # ISO/IEC 9899:1999, 6.4.1 + c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) + # ISO/IEC 9899:2011, 6.4.1 + c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn', + '_Static_assert', '_Thread_local']) + # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html + # excluding _.* + gcc_words = set(['asm', 'typeof']) + if name in c89_words | c99_words | c11_words | gcc_words: + return "q_" + name return name.replace('-', '_').lstrip("*") def c_fun(name): diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c index 88ca9bb1b7..24952061cf 100644 --- a/target-arm/arm-semi.c +++ b/target-arm/arm-semi.c @@ -281,7 +281,7 @@ uint32_t do_arm_semihosting(CPUARMState *env) return len - ret; } case TARGET_SYS_READC: - /* XXX: Read from debug cosole. Not implemented. */ + /* XXX: Read from debug console. Not implemented. */ return 0; case TARGET_SYS_ISTTY: if (use_gdb_syscalls()) { diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 191895cca8..d7f93d98f0 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -79,7 +79,7 @@ struct arm_boot_info; typedef struct CPUARMState { /* Regs for current mode. */ uint32_t regs[16]; - /* Frequently accessed CPSR bits are stored separately for efficiently. + /* Frequently accessed CPSR bits are stored separately for efficiency. This contains all the other bits. Use cpsr_{read,write} to access the whole CPSR. */ uint32_t uncached_cpsr; diff --git a/target-arm/helper.c b/target-arm/helper.c index 5727da296c..dceaa95c80 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -988,7 +988,7 @@ static void ttbr164_reset(CPUARMState *env, const ARMCPRegInfo *ri) } static const ARMCPRegInfo lpae_cp_reginfo[] = { - /* NOP AMAIR0/1: the override is because these clash with tha rather + /* NOP AMAIR0/1: the override is because these clash with the rather * broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo. */ { .name = "AMAIR0", .cp = 15, .crn = 10, .crm = 3, .opc1 = 0, .opc2 = 0, @@ -2899,8 +2899,8 @@ uint32_t HELPER(logicq_cc)(uint64_t val) return (val >> 32) | (val != 0); } -/* VFP support. We follow the convention used for VFP instrunctions: - Single precition routines have a "s" suffix, double precision a +/* VFP support. We follow the convention used for VFP instructions: + Single precision routines have a "s" suffix, double precision a "d" suffix. */ /* Convert host exception flags to vfp form. */ diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c index e0b9dbf67e..8bb5129d6a 100644 --- a/target-arm/neon_helper.c +++ b/target-arm/neon_helper.c @@ -530,7 +530,7 @@ NEON_VOP(rshl_s16, neon_s16, 2) #undef NEON_FN /* The addition of the rounding constant may overflow, so we use an - * intermediate 64 bits accumulator. */ + * intermediate 64 bit accumulator. */ uint32_t HELPER(neon_rshl_s32)(uint32_t valop, uint32_t shiftop) { int32_t dest; @@ -547,8 +547,8 @@ uint32_t HELPER(neon_rshl_s32)(uint32_t valop, uint32_t shiftop) return dest; } -/* Handling addition overflow with 64 bits inputs values is more - * tricky than with 32 bits values. */ +/* Handling addition overflow with 64 bit input values is more + * tricky than with 32 bit values. */ uint64_t HELPER(neon_rshl_s64)(uint64_t valop, uint64_t shiftop) { int8_t shift = (int8_t)shiftop; @@ -590,7 +590,7 @@ NEON_VOP(rshl_u16, neon_u16, 2) #undef NEON_FN /* The addition of the rounding constant may overflow, so we use an - * intermediate 64 bits accumulator. */ + * intermediate 64 bit accumulator. */ uint32_t HELPER(neon_rshl_u32)(uint32_t val, uint32_t shiftop) { uint32_t dest; @@ -608,8 +608,8 @@ uint32_t HELPER(neon_rshl_u32)(uint32_t val, uint32_t shiftop) return dest; } -/* Handling addition overflow with 64 bits inputs values is more - * tricky than with 32 bits values. */ +/* Handling addition overflow with 64 bit input values is more + * tricky than with 32 bit values. */ uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shiftop) { int8_t shift = (uint8_t)shiftop; @@ -817,7 +817,7 @@ NEON_VOP_ENV(qrshl_u16, neon_u16, 2) #undef NEON_FN /* The addition of the rounding constant may overflow, so we use an - * intermediate 64 bits accumulator. */ + * intermediate 64 bit accumulator. */ uint32_t HELPER(neon_qrshl_u32)(CPUARMState *env, uint32_t val, uint32_t shiftop) { uint32_t dest; @@ -846,8 +846,8 @@ uint32_t HELPER(neon_qrshl_u32)(CPUARMState *env, uint32_t val, uint32_t shiftop return dest; } -/* Handling addition overflow with 64 bits inputs values is more - * tricky than with 32 bits values. */ +/* Handling addition overflow with 64 bit input values is more + * tricky than with 32 bit values. */ uint64_t HELPER(neon_qrshl_u64)(CPUARMState *env, uint64_t val, uint64_t shiftop) { int8_t shift = (int8_t)shiftop; @@ -914,7 +914,7 @@ NEON_VOP_ENV(qrshl_s16, neon_s16, 2) #undef NEON_FN /* The addition of the rounding constant may overflow, so we use an - * intermediate 64 bits accumulator. */ + * intermediate 64 bit accumulator. */ uint32_t HELPER(neon_qrshl_s32)(CPUARMState *env, uint32_t valop, uint32_t shiftop) { int32_t dest; @@ -942,8 +942,8 @@ uint32_t HELPER(neon_qrshl_s32)(CPUARMState *env, uint32_t valop, uint32_t shift return dest; } -/* Handling addition overflow with 64 bits inputs values is more - * tricky than with 32 bits values. */ +/* Handling addition overflow with 64 bit input values is more + * tricky than with 32 bit values. */ uint64_t HELPER(neon_qrshl_s64)(CPUARMState *env, uint64_t valop, uint64_t shiftop) { int8_t shift = (uint8_t)shiftop; @@ -1671,7 +1671,7 @@ uint64_t HELPER(neon_negl_u64)(uint64_t x) return -x; } -/* Saturnating sign manuipulation. */ +/* Saturating sign manipulation. */ /* ??? Make these use NEON_VOP1 */ #define DO_QABS8(x) do { \ if (x == (int8_t)0x80) { \ diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 490111c22f..d77bfab771 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -99,7 +99,7 @@ void tlb_fill(CPUARMState *env1, target_ulong addr, int is_write, int mmu_idx, } #endif -/* FIXME: Pass an axplicit pointer to QF to CPUARMState, and move saturating +/* FIXME: Pass an explicit pointer to QF to CPUARMState, and move saturating instructions into helper.c */ uint32_t HELPER(add_setq)(uint32_t a, uint32_t b) { diff --git a/target-arm/translate.c b/target-arm/translate.c index 29008a4b34..edef79a2cf 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -53,7 +53,7 @@ typedef struct DisasContext { int condjmp; /* The label that will be jumped to when the instruction is skipped. */ int condlabel; - /* Thumb-2 condtional execution bits. */ + /* Thumb-2 conditional execution bits. */ int condexec_mask; int condexec_cond; struct TranslationBlock *tb; @@ -77,7 +77,7 @@ static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE]; #endif /* These instructions trap after executing, so defer them until after the - conditional executions state has been updated. */ + conditional execution state has been updated. */ #define DISAS_WFI 4 #define DISAS_SWI 5 @@ -155,7 +155,7 @@ static void load_reg_var(DisasContext *s, TCGv var, int reg) { if (reg == 15) { uint32_t addr; - /* normaly, since we updated PC, we need only to add one insn */ + /* normally, since we updated PC, we need only to add one insn */ if (s->thumb) addr = (long)s->pc + 2; else @@ -4897,7 +4897,7 @@ static int disas_neon_data_insn(CPUARMState * env, DisasContext *s, uint32_t ins size--; } shift = (insn >> 16) & ((1 << (3 + size)) - 1); - /* To avoid excessive dumplication of ops we implement shift + /* To avoid excessive duplication of ops we implement shift by immediate using the variable shift operations. */ if (op < 8) { /* Shift by immediate: @@ -6402,7 +6402,7 @@ static void gen_logicq_cc(TCGv_i64 val) /* Load/Store exclusive instructions are implemented by remembering the value/address loaded, and seeing if these are the same - when the store is performed. This should be is sufficient to implement + when the store is performed. This should be sufficient to implement the architecturally mandated semantics, and avoids having to monitor regular stores. @@ -9892,7 +9892,7 @@ static inline void gen_intermediate_code_internal(CPUARMState *env, } else { /* While branches must always occur at the end of an IT block, there are a few other things that can cause us to terminate - the TB in the middel of an IT block: + the TB in the middle of an IT block: - Exception generating instructions (bkpt, swi, undefined). - Page boundaries. - Hardware watchpoints. diff --git a/target-i386/Makefile.objs b/target-i386/Makefile.objs index 683fd59af9..0715f58a8e 100644 --- a/target-i386/Makefile.objs +++ b/target-i386/Makefile.objs @@ -3,6 +3,7 @@ obj-y += excp_helper.o fpu_helper.o cc_helper.o int_helper.o svm_helper.o obj-y += smm_helper.o misc_helper.o mem_helper.o seg_helper.o obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o obj-$(CONFIG_KVM) += kvm.o hyperv.o +obj-$(CONFIG_NO_KVM) += kvm-stub.o obj-$(CONFIG_LINUX_USER) += ioport-user.o obj-$(CONFIG_BSD_USER) += ioport-user.o diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 857b94ea87..6d5d0d6e10 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -28,6 +28,7 @@ #include "qemu-config.h" #include "qapi/qapi-visit-core.h" +#include "qmp-commands.h" #include "hyperv.h" @@ -1125,6 +1126,27 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf, const char *optarg) } } +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + x86_def_t *def; + + for (def = x86_defs; def; def = def->next) { + CpuDefinitionInfoList *entry; + CpuDefinitionInfo *info; + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(def->name); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = cpu_list; + cpu_list = entry; + } + + return cpu_list; +} + int cpu_x86_register(X86CPU *cpu, const char *cpu_model) { CPUX86State *env = &cpu->env; @@ -1746,6 +1768,7 @@ static void x86_cpu_initfn(Object *obj) { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; + static int inited; cpu_exec_init(env); @@ -1775,6 +1798,15 @@ static void x86_cpu_initfn(Object *obj) x86_cpuid_set_tsc_freq, NULL, NULL, NULL); env->cpuid_apic_id = env->cpu_index; + + /* init various static tables used in TCG mode */ + if (tcg_enabled() && !inited) { + inited = 1; + optimize_flags_init(); +#ifndef CONFIG_USER_ONLY + cpu_set_debug_excp_handler(breakpoint_handler); +#endif + } } static void x86_cpu_common_class_init(ObjectClass *oc, void *data) diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 2a61c810bb..60f9e972bd 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -935,6 +935,7 @@ static inline int hw_breakpoint_len(unsigned long dr7, int index) void hw_breakpoint_insert(CPUX86State *env, int index); void hw_breakpoint_remove(CPUX86State *env, int index); int check_hw_breakpoints(CPUX86State *env, int force_dr6_update); +void breakpoint_handler(CPUX86State *env); /* will be suppressed */ void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0); diff --git a/target-i386/helper.c b/target-i386/helper.c index b748d90063..8a5da3d7c0 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -941,9 +941,7 @@ int check_hw_breakpoints(CPUX86State *env, int force_dr6_update) return hit_enabled; } -static CPUDebugExcpHandler *prev_debug_excp_handler; - -static void breakpoint_handler(CPUX86State *env) +void breakpoint_handler(CPUX86State *env) { CPUBreakpoint *bp; @@ -965,8 +963,6 @@ static void breakpoint_handler(CPUX86State *env) break; } } - if (prev_debug_excp_handler) - prev_debug_excp_handler(env); } typedef struct MCEInjectionParams { @@ -1155,21 +1151,11 @@ X86CPU *cpu_x86_init(const char *cpu_model) { X86CPU *cpu; CPUX86State *env; - static int inited; cpu = X86_CPU(object_new(TYPE_X86_CPU)); env = &cpu->env; env->cpu_model_str = cpu_model; - /* init various static tables used in TCG mode */ - if (tcg_enabled() && !inited) { - inited = 1; - optimize_flags_init(); -#ifndef CONFIG_USER_ONLY - prev_debug_excp_handler = - cpu_set_debug_excp_handler(breakpoint_handler); -#endif - } if (cpu_x86_register(cpu, cpu_model) < 0) { object_delete(OBJECT(cpu)); return NULL; diff --git a/target-i386/kvm-stub.c b/target-i386/kvm-stub.c new file mode 100644 index 0000000000..11429c461e --- /dev/null +++ b/target-i386/kvm-stub.c @@ -0,0 +1,18 @@ +/* + * QEMU KVM x86 specific function stubs + * + * Copyright Linaro Limited 2012 + * + * Author: Peter Maydell <peter.maydell@linaro.org> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ +#include "qemu-common.h" +#include "kvm_i386.h" + +bool kvm_allows_irq0_override(void) +{ + return 1; +} diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 4cfb3faf01..696b14a04a 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -23,6 +23,7 @@ #include "qemu-common.h" #include "sysemu.h" #include "kvm.h" +#include "kvm_i386.h" #include "cpu.h" #include "gdbstub.h" #include "host-utils.h" @@ -65,6 +66,11 @@ static bool has_msr_async_pf_en; static bool has_msr_misc_enable; static int lm_capable_kernel; +bool kvm_allows_irq0_override(void) +{ + return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing(); +} + static struct kvm_cpuid2 *try_get_cpuid(KVMState *s, int max) { struct kvm_cpuid2 *cpuid; @@ -2041,4 +2047,11 @@ void kvm_arch_init_irq_routing(KVMState *s) */ no_hpet = 1; } + /* We know at this point that we're using the in-kernel + * irqchip, so we can use irqfds, and on x86 we know + * we can use msi via irqfd and GSI routing. + */ + kvm_irqfds_allowed = true; + kvm_msi_via_irqfd_allowed = true; + kvm_gsi_routing_allowed = true; } diff --git a/target-i386/kvm_i386.h b/target-i386/kvm_i386.h new file mode 100644 index 0000000000..b82bbf401e --- /dev/null +++ b/target-i386/kvm_i386.h @@ -0,0 +1,16 @@ +/* + * QEMU KVM support -- x86 specific functions. + * + * Copyright (c) 2012 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_KVM_I386_H +#define QEMU_KVM_I386_H + +bool kvm_allows_irq0_override(void); + +#endif diff --git a/target-mips/translate.c b/target-mips/translate.c index 4e15ee36b8..47daf8574f 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -12763,6 +12763,7 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_SRSConf3 = env->cpu_model->CP0_SRSConf3; env->CP0_SRSConf4_rw_bitmask = env->cpu_model->CP0_SRSConf4_rw_bitmask; env->CP0_SRSConf4 = env->cpu_model->CP0_SRSConf4; + env->active_fpu.fcr0 = env->cpu_model->CP1_fcr0; env->insn_flags = env->cpu_model->insn_flags; #if defined(CONFIG_USER_ONLY) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 5742229197..6fe4168dc0 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -27,6 +27,7 @@ #include "gdbstub.h" #include <kvm.h> #include "kvm_ppc.h" +#include "qmp-commands.h" //#define PPC_DUMP_CPU //#define PPC_DEBUG_SPR @@ -10345,6 +10346,31 @@ void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf) } } +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(ppc_defs); i++) { + CpuDefinitionInfoList *entry; + CpuDefinitionInfo *info; + + if (!ppc_cpu_usable(&ppc_defs[i])) { + continue; + } + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(ppc_defs[i].name); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = cpu_list; + cpu_list = entry; + } + + return cpu_list; +} + /* CPUClass::reset() */ static void ppc_cpu_reset(CPUState *s) { diff --git a/target-unicore32/Makefile.objs b/target-unicore32/Makefile.objs index 2e0e093e1f..777f01fef8 100644 --- a/target-unicore32/Makefile.objs +++ b/target-unicore32/Makefile.objs @@ -1,4 +1,6 @@ obj-y += translate.o op_helper.o helper.o cpu.o -obj-$(CONFIG_SOFTMMU) += machine.o +obj-y += ucf64_helper.o + +obj-$(CONFIG_SOFTMMU) += machine.o softmmu.o $(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS) diff --git a/target-unicore32/cpu.c b/target-unicore32/cpu.c index de63f58dda..3425bbeac9 100644 --- a/target-unicore32/cpu.c +++ b/target-unicore32/cpu.c @@ -1,7 +1,7 @@ /* * QEMU UniCore32 CPU * - * Copyright (c) 2010-2011 GUAN Xue-tao + * Copyright (c) 2010-2012 Guan Xuetao * Copyright (c) 2012 SUSE LINUX Products GmbH * * This program is free software; you can redistribute it and/or modify @@ -32,13 +32,16 @@ static void unicore_ii_cpu_initfn(Object *obj) UniCore32CPU *cpu = UNICORE32_CPU(obj); CPUUniCore32State *env = &cpu->env; - env->cp0.c0_cpuid = 0x40010863; + env->cp0.c0_cpuid = 0x4d000863; + env->cp0.c0_cachetype = 0x0d152152; + env->cp0.c1_sys = 0x2000; + env->cp0.c2_base = 0x0; + env->cp0.c3_faultstatus = 0x0; + env->cp0.c4_faultaddr = 0x0; + env->ucf64.xregs[UC32_UCF64_FPSCR] = 0; set_feature(env, UC32_HWCAP_CMOV); set_feature(env, UC32_HWCAP_UCF64); - env->ucf64.xregs[UC32_UCF64_FPSCR] = 0; - env->cp0.c0_cachetype = 0x1dd20d2; - env->cp0.c1_sys = 0x00090078; } static void uc32_any_cpu_initfn(Object *obj) @@ -47,6 +50,7 @@ static void uc32_any_cpu_initfn(Object *obj) CPUUniCore32State *env = &cpu->env; env->cp0.c0_cpuid = 0xffffffff; + env->ucf64.xregs[UC32_UCF64_FPSCR] = 0; set_feature(env, UC32_HWCAP_CMOV); set_feature(env, UC32_HWCAP_UCF64); @@ -65,8 +69,13 @@ static void uc32_cpu_initfn(Object *obj) cpu_exec_init(env); env->cpu_model_str = object_get_typename(obj); +#ifdef CONFIG_USER_ONLY env->uncached_asr = ASR_MODE_USER; env->regs[31] = 0; +#else + env->uncached_asr = ASR_MODE_PRIV; + env->regs[31] = 0x03000000; +#endif tlb_flush(env, 1); } diff --git a/target-unicore32/cpu.h b/target-unicore32/cpu.h index 81c14ffd77..06508a1278 100644 --- a/target-unicore32/cpu.h +++ b/target-unicore32/cpu.h @@ -1,15 +1,15 @@ /* * UniCore32 virtual CPU header * - * Copyright (C) 2010-2011 GUAN Xue-tao + * Copyright (C) 2010-2012 Guan Xuetao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation, or (at your option) any * later version. See the COPYING file in the top-level directory. */ -#ifndef __CPU_UC32_H__ -#define __CPU_UC32_H__ +#ifndef QEMU_UNICORE32_CPU_H +#define QEMU_UNICORE32_CPU_H #define TARGET_LONG_BITS 32 #define TARGET_PAGE_BITS 12 @@ -89,8 +89,10 @@ typedef struct CPUUniCore32State { #define ASR_NZCV (ASR_N | ASR_Z | ASR_C | ASR_V) #define ASR_RESERVED (~(ASR_M | ASR_I | ASR_NZCV)) -#define UC32_EXCP_PRIV (ASR_MODE_PRIV) -#define UC32_EXCP_TRAP (ASR_MODE_TRAP) +#define UC32_EXCP_PRIV (1) +#define UC32_EXCP_ITRAP (2) +#define UC32_EXCP_DTRAP (3) +#define UC32_EXCP_INTR (4) /* Return the current ASR value. */ target_ulong cpu_asr_read(CPUUniCore32State *env1); @@ -120,10 +122,6 @@ void cpu_asr_write(CPUUniCore32State *env1, target_ulong val, target_ulong mask) #define UC32_HWCAP_CMOV 4 /* 1 << 2 */ #define UC32_HWCAP_UCF64 8 /* 1 << 3 */ -#define UC32_CPUID(env) (env->cp0.c0_cpuid) -#define UC32_CPUID_UCV2 0x40010863 -#define UC32_CPUID_ANY 0xffffffff - #define cpu_init uc32_cpu_init #define cpu_exec uc32_cpu_exec #define cpu_signal_handler uc32_cpu_signal_handler @@ -189,4 +187,4 @@ static inline bool cpu_has_work(CPUUniCore32State *env) (CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXITTB); } -#endif /* __CPU_UC32_H__ */ +#endif /* QEMU_UNICORE32_CPU_H */ diff --git a/target-unicore32/helper.c b/target-unicore32/helper.c index 9fe4a375e4..a9e226bde4 100644 --- a/target-unicore32/helper.c +++ b/target-unicore32/helper.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2011 GUAN Xue-tao + * Copyright (C) 2010-2012 Guan Xuetao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -13,6 +13,15 @@ #include "gdbstub.h" #include "helper.h" #include "host-utils.h" +#include "console.h" + +#undef DEBUG_UC32 + +#ifdef DEBUG_UC32 +#define DPRINTF(fmt, ...) printf("%s: " fmt , __func__, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif CPUUniCore32State *uc32_cpu_init(const char *cpu_model) { @@ -45,389 +54,203 @@ uint32_t HELPER(clz)(uint32_t x) return clz32(x); } -void do_interrupt(CPUUniCore32State *env) -{ - env->exception_index = -1; -} - -int uc32_cpu_handle_mmu_fault(CPUUniCore32State *env, target_ulong address, int rw, - int mmu_idx) -{ - env->exception_index = UC32_EXCP_TRAP; - env->cp0.c4_faultaddr = address; - return 1; -} - -/* These should probably raise undefined insn exceptions. */ -void HELPER(set_cp)(CPUUniCore32State *env, uint32_t insn, uint32_t val) -{ - int op1 = (insn >> 8) & 0xf; - cpu_abort(env, "cp%i insn %08x\n", op1, insn); - return; -} - -uint32_t HELPER(get_cp)(CPUUniCore32State *env, uint32_t insn) -{ - int op1 = (insn >> 8) & 0xf; - cpu_abort(env, "cp%i insn %08x\n", op1, insn); - return 0; -} - -void HELPER(set_cp0)(CPUUniCore32State *env, uint32_t insn, uint32_t val) -{ - cpu_abort(env, "cp0 insn %08x\n", insn); -} - -uint32_t HELPER(get_cp0)(CPUUniCore32State *env, uint32_t insn) -{ - cpu_abort(env, "cp0 insn %08x\n", insn); - return 0; -} - -void switch_mode(CPUUniCore32State *env, int mode) -{ - if (mode != ASR_MODE_USER) { - cpu_abort(env, "Tried to switch out of user mode\n"); - } -} - -void HELPER(set_r29_banked)(CPUUniCore32State *env, uint32_t mode, uint32_t val) -{ - cpu_abort(env, "banked r29 write\n"); -} - -uint32_t HELPER(get_r29_banked)(CPUUniCore32State *env, uint32_t mode) -{ - cpu_abort(env, "banked r29 read\n"); - return 0; -} - -/* UniCore-F64 support. We follow the convention used for F64 instrunctions: - Single precition routines have a "s" suffix, double precision a - "d" suffix. */ - -/* Convert host exception flags to f64 form. */ -static inline int ucf64_exceptbits_from_host(int host_bits) -{ - int target_bits = 0; - - if (host_bits & float_flag_invalid) { - target_bits |= UCF64_FPSCR_FLAG_INVALID; - } - if (host_bits & float_flag_divbyzero) { - target_bits |= UCF64_FPSCR_FLAG_DIVZERO; - } - if (host_bits & float_flag_overflow) { - target_bits |= UCF64_FPSCR_FLAG_OVERFLOW; - } - if (host_bits & float_flag_underflow) { - target_bits |= UCF64_FPSCR_FLAG_UNDERFLOW; - } - if (host_bits & float_flag_inexact) { - target_bits |= UCF64_FPSCR_FLAG_INEXACT; - } - return target_bits; -} - -uint32_t HELPER(ucf64_get_fpscr)(CPUUniCore32State *env) -{ - int i; - uint32_t fpscr; - - fpscr = (env->ucf64.xregs[UC32_UCF64_FPSCR] & UCF64_FPSCR_MASK); - i = get_float_exception_flags(&env->ucf64.fp_status); - fpscr |= ucf64_exceptbits_from_host(i); - return fpscr; -} - -/* Convert ucf64 exception flags to target form. */ -static inline int ucf64_exceptbits_to_host(int target_bits) -{ - int host_bits = 0; - - if (target_bits & UCF64_FPSCR_FLAG_INVALID) { - host_bits |= float_flag_invalid; - } - if (target_bits & UCF64_FPSCR_FLAG_DIVZERO) { - host_bits |= float_flag_divbyzero; - } - if (target_bits & UCF64_FPSCR_FLAG_OVERFLOW) { - host_bits |= float_flag_overflow; - } - if (target_bits & UCF64_FPSCR_FLAG_UNDERFLOW) { - host_bits |= float_flag_underflow; - } - if (target_bits & UCF64_FPSCR_FLAG_INEXACT) { - host_bits |= float_flag_inexact; - } - return host_bits; -} - -void HELPER(ucf64_set_fpscr)(CPUUniCore32State *env, uint32_t val) -{ - int i; - uint32_t changed; - - changed = env->ucf64.xregs[UC32_UCF64_FPSCR]; - env->ucf64.xregs[UC32_UCF64_FPSCR] = (val & UCF64_FPSCR_MASK); - - changed ^= val; - if (changed & (UCF64_FPSCR_RND_MASK)) { - i = UCF64_FPSCR_RND(val); - switch (i) { - case 0: - i = float_round_nearest_even; - break; - case 1: - i = float_round_to_zero; - break; - case 2: - i = float_round_up; - break; - case 3: - i = float_round_down; - break; - default: /* 100 and 101 not implement */ - cpu_abort(env, "Unsupported UniCore-F64 round mode"); - } - set_float_rounding_mode(i, &env->ucf64.fp_status); - } - - i = ucf64_exceptbits_to_host(UCF64_FPSCR_TRAPEN(val)); - set_float_exception_flags(i, &env->ucf64.fp_status); -} - -float32 HELPER(ucf64_adds)(float32 a, float32 b, CPUUniCore32State *env) -{ - return float32_add(a, b, &env->ucf64.fp_status); -} - -float64 HELPER(ucf64_addd)(float64 a, float64 b, CPUUniCore32State *env) -{ - return float64_add(a, b, &env->ucf64.fp_status); -} - -float32 HELPER(ucf64_subs)(float32 a, float32 b, CPUUniCore32State *env) -{ - return float32_sub(a, b, &env->ucf64.fp_status); -} - -float64 HELPER(ucf64_subd)(float64 a, float64 b, CPUUniCore32State *env) -{ - return float64_sub(a, b, &env->ucf64.fp_status); -} - -float32 HELPER(ucf64_muls)(float32 a, float32 b, CPUUniCore32State *env) -{ - return float32_mul(a, b, &env->ucf64.fp_status); -} - -float64 HELPER(ucf64_muld)(float64 a, float64 b, CPUUniCore32State *env) -{ - return float64_mul(a, b, &env->ucf64.fp_status); -} - -float32 HELPER(ucf64_divs)(float32 a, float32 b, CPUUniCore32State *env) -{ - return float32_div(a, b, &env->ucf64.fp_status); -} - -float64 HELPER(ucf64_divd)(float64 a, float64 b, CPUUniCore32State *env) -{ - return float64_div(a, b, &env->ucf64.fp_status); -} - -float32 HELPER(ucf64_negs)(float32 a) -{ - return float32_chs(a); -} - -float64 HELPER(ucf64_negd)(float64 a) -{ - return float64_chs(a); -} - -float32 HELPER(ucf64_abss)(float32 a) -{ - return float32_abs(a); -} - -float64 HELPER(ucf64_absd)(float64 a) -{ - return float64_abs(a); -} - -/* XXX: check quiet/signaling case */ -void HELPER(ucf64_cmps)(float32 a, float32 b, uint32_t c, CPUUniCore32State *env) -{ - int flag; - flag = float32_compare_quiet(a, b, &env->ucf64.fp_status); - env->CF = 0; - switch (c & 0x7) { - case 0: /* F */ - break; - case 1: /* UN */ - if (flag == 2) { - env->CF = 1; +#ifndef CONFIG_USER_ONLY +void helper_cp0_set(CPUUniCore32State *env, uint32_t val, uint32_t creg, + uint32_t cop) +{ + /* + * movc pp.nn, rn, #imm9 + * rn: UCOP_REG_D + * nn: UCOP_REG_N + * 1: sys control reg. + * 2: page table base reg. + * 3: data fault status reg. + * 4: insn fault status reg. + * 5: cache op. reg. + * 6: tlb op. reg. + * imm9: split UCOP_IMM10 with bit5 is 0 + */ + switch (creg) { + case 1: + if (cop != 0) { + goto unrecognized; } + env->cp0.c1_sys = val; break; - case 2: /* EQ */ - if (flag == 0) { - env->CF = 1; + case 2: + if (cop != 0) { + goto unrecognized; } + env->cp0.c2_base = val; break; - case 3: /* UEQ */ - if ((flag == 0) || (flag == 2)) { - env->CF = 1; + case 3: + if (cop != 0) { + goto unrecognized; } + env->cp0.c3_faultstatus = val; break; - case 4: /* OLT */ - if (flag == -1) { - env->CF = 1; + case 4: + if (cop != 0) { + goto unrecognized; } + env->cp0.c4_faultaddr = val; break; - case 5: /* ULT */ - if ((flag == -1) || (flag == 2)) { - env->CF = 1; + case 5: + switch (cop) { + case 28: + DPRINTF("Invalidate Entire I&D cache\n"); + return; + case 20: + DPRINTF("Invalidate Entire Icache\n"); + return; + case 12: + DPRINTF("Invalidate Entire Dcache\n"); + return; + case 10: + DPRINTF("Clean Entire Dcache\n"); + return; + case 14: + DPRINTF("Flush Entire Dcache\n"); + return; + case 13: + DPRINTF("Invalidate Dcache line\n"); + return; + case 11: + DPRINTF("Clean Dcache line\n"); + return; + case 15: + DPRINTF("Flush Dcache line\n"); + return; } break; - case 6: /* OLE */ - if ((flag == -1) || (flag == 0)) { - env->CF = 1; - } - break; - case 7: /* ULE */ - if (flag != 1) { - env->CF = 1; + case 6: + if ((cop <= 6) && (cop >= 2)) { + /* invalid all tlb */ + tlb_flush(env, 1); + return; } break; + default: + goto unrecognized; } - env->ucf64.xregs[UC32_UCF64_FPSCR] = (env->CF << 29) - | (env->ucf64.xregs[UC32_UCF64_FPSCR] & 0x0fffffff); -} - -void HELPER(ucf64_cmpd)(float64 a, float64 b, uint32_t c, CPUUniCore32State *env) -{ - int flag; - flag = float64_compare_quiet(a, b, &env->ucf64.fp_status); - env->CF = 0; - switch (c & 0x7) { - case 0: /* F */ - break; - case 1: /* UN */ - if (flag == 2) { - env->CF = 1; - } - break; - case 2: /* EQ */ - if (flag == 0) { - env->CF = 1; - } - break; - case 3: /* UEQ */ - if ((flag == 0) || (flag == 2)) { - env->CF = 1; + return; +unrecognized: + DPRINTF("Wrong register (%d) or wrong operation (%d) in cp0_set!\n", + creg, cop); +} + +uint32_t helper_cp0_get(CPUUniCore32State *env, uint32_t creg, uint32_t cop) +{ + /* + * movc rd, pp.nn, #imm9 + * rd: UCOP_REG_D + * nn: UCOP_REG_N + * 0: cpuid and cachetype + * 1: sys control reg. + * 2: page table base reg. + * 3: data fault status reg. + * 4: insn fault status reg. + * imm9: split UCOP_IMM10 with bit5 is 0 + */ + switch (creg) { + case 0: + switch (cop) { + case 0: + return env->cp0.c0_cpuid; + case 1: + return env->cp0.c0_cachetype; } break; - case 4: /* OLT */ - if (flag == -1) { - env->CF = 1; + case 1: + if (cop == 0) { + return env->cp0.c1_sys; } break; - case 5: /* ULT */ - if ((flag == -1) || (flag == 2)) { - env->CF = 1; + case 2: + if (cop == 0) { + return env->cp0.c2_base; } break; - case 6: /* OLE */ - if ((flag == -1) || (flag == 0)) { - env->CF = 1; + case 3: + if (cop == 0) { + return env->cp0.c3_faultstatus; } break; - case 7: /* ULE */ - if (flag != 1) { - env->CF = 1; + case 4: + if (cop == 0) { + return env->cp0.c4_faultaddr; } break; } - env->ucf64.xregs[UC32_UCF64_FPSCR] = (env->CF << 29) - | (env->ucf64.xregs[UC32_UCF64_FPSCR] & 0x0fffffff); -} - -/* Helper routines to perform bitwise copies between float and int. */ -static inline float32 ucf64_itos(uint32_t i) -{ - union { - uint32_t i; - float32 s; - } v; - - v.i = i; - return v.s; -} - -static inline uint32_t ucf64_stoi(float32 s) -{ - union { - uint32_t i; - float32 s; - } v; - - v.s = s; - return v.i; -} - -static inline float64 ucf64_itod(uint64_t i) -{ - union { - uint64_t i; - float64 d; - } v; - - v.i = i; - return v.d; + DPRINTF("Wrong register (%d) or wrong operation (%d) in cp0_set!\n", + creg, cop); + return 0; } -static inline uint64_t ucf64_dtoi(float64 d) +#ifdef CONFIG_CURSES +/* + * FIXME: + * 1. curses windows will be blank when switching back + * 2. backspace is not handled yet + */ +static void putc_on_screen(unsigned char ch) { - union { - uint64_t i; - float64 d; - } v; + static WINDOW *localwin; + static int init; - v.d = d; - return v.i; -} + if (!init) { + /* Assume 80 * 30 screen to minimize the implementation */ + localwin = newwin(30, 80, 0, 0); + scrollok(localwin, TRUE); + init = TRUE; + } -/* Integer to float conversion. */ -float32 HELPER(ucf64_si2sf)(float32 x, CPUUniCore32State *env) -{ - return int32_to_float32(ucf64_stoi(x), &env->ucf64.fp_status); -} + if (isprint(ch)) { + wprintw(localwin, "%c", ch); + } else { + switch (ch) { + case '\n': + wprintw(localwin, "%c", ch); + break; + case '\r': + /* If '\r' is put before '\n', the curses window will destroy the + * last print line. And meanwhile, '\n' implifies '\r' inside. */ + break; + default: /* Not handled, so just print it hex code */ + wprintw(localwin, "-- 0x%x --", ch); + } + } -float64 HELPER(ucf64_si2df)(float32 x, CPUUniCore32State *env) -{ - return int32_to_float64(ucf64_stoi(x), &env->ucf64.fp_status); + wrefresh(localwin); } +#else +#define putc_on_screen(c) do { } while (0) +#endif -/* Float to integer conversion. */ -float32 HELPER(ucf64_sf2si)(float32 x, CPUUniCore32State *env) +void helper_cp1_putc(target_ulong x) { - return ucf64_itos(float32_to_int32(x, &env->ucf64.fp_status)); + putc_on_screen((unsigned char)x); /* Output to screen */ + DPRINTF("%c", x); /* Output to stdout */ } +#endif -float32 HELPER(ucf64_df2si)(float64 x, CPUUniCore32State *env) +#ifdef CONFIG_USER_ONLY +void switch_mode(CPUUniCore32State *env, int mode) { - return ucf64_itos(float64_to_int32(x, &env->ucf64.fp_status)); + if (mode != ASR_MODE_USER) { + cpu_abort(env, "Tried to switch out of user mode\n"); + } } -/* floating point conversion */ -float64 HELPER(ucf64_sf2df)(float32 x, CPUUniCore32State *env) +void do_interrupt(CPUUniCore32State *env) { - return float32_to_float64(x, &env->ucf64.fp_status); + cpu_abort(env, "NO interrupt in user mode\n"); } -float32 HELPER(ucf64_df2sf)(float64 x, CPUUniCore32State *env) +int uc32_cpu_handle_mmu_fault(CPUUniCore32State *env, target_ulong address, + int access_type, int mmu_idx) { - return float64_to_float32(x, &env->ucf64.fp_status); + cpu_abort(env, "NO mmu fault in user mode\n"); + return 1; } +#endif diff --git a/target-unicore32/helper.h b/target-unicore32/helper.h index 5a3b8a41ea..305318ae59 100644 --- a/target-unicore32/helper.h +++ b/target-unicore32/helper.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2011 GUAN Xue-tao + * Copyright (C) 2010-2012 Guan Xuetao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -8,6 +8,12 @@ */ #include "def-helper.h" +#ifndef CONFIG_USER_ONLY +DEF_HELPER_4(cp0_set, void, env, i32, i32, i32) +DEF_HELPER_3(cp0_get, i32, env, i32, i32) +DEF_HELPER_1(cp1_putc, void, i32) +#endif + DEF_HELPER_1(clz, i32, i32) DEF_HELPER_1(clo, i32, i32) @@ -16,12 +22,6 @@ DEF_HELPER_1(exception, void, i32) DEF_HELPER_2(asr_write, void, i32, i32) DEF_HELPER_0(asr_read, i32) -DEF_HELPER_3(set_cp0, void, env, i32, i32) -DEF_HELPER_2(get_cp0, i32, env, i32) - -DEF_HELPER_3(set_cp, void, env, i32, i32) -DEF_HELPER_2(get_cp, i32, env, i32) - DEF_HELPER_1(get_user_reg, i32, i32) DEF_HELPER_2(set_user_reg, void, i32, i32) @@ -38,9 +38,6 @@ DEF_HELPER_2(shr_cc, i32, i32, i32) DEF_HELPER_2(sar_cc, i32, i32, i32) DEF_HELPER_2(ror_cc, i32, i32, i32) -DEF_HELPER_2(get_r29_banked, i32, env, i32) -DEF_HELPER_3(set_r29_banked, void, env, i32, i32) - DEF_HELPER_1(ucf64_get_fpscr, i32, env) DEF_HELPER_2(ucf64_set_fpscr, void, env, i32) diff --git a/target-unicore32/machine.c b/target-unicore32/machine.c new file mode 100644 index 0000000000..60b2ec1771 --- /dev/null +++ b/target-unicore32/machine.c @@ -0,0 +1,23 @@ +/* + * Generic machine functions for UniCore32 ISA + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "hw/hw.h" + +void cpu_save(QEMUFile *f, void *opaque) +{ + hw_error("%s not supported yet.\n", __func__); +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + hw_error("%s not supported yet.\n", __func__); + + return 0; +} diff --git a/target-unicore32/op_helper.c b/target-unicore32/op_helper.c index b954c30a84..c63789d6cb 100644 --- a/target-unicore32/op_helper.c +++ b/target-unicore32/op_helper.c @@ -1,7 +1,7 @@ /* * UniCore32 helper routines * - * Copyright (C) 2010-2011 GUAN Xue-tao + * Copyright (C) 2010-2012 Guan Xuetao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -248,3 +248,45 @@ uint32_t HELPER(ror_cc)(uint32_t x, uint32_t i) return ((uint32_t)x >> shift) | (x << (32 - shift)); } } + +#ifndef CONFIG_USER_ONLY +#define MMUSUFFIX _mmu + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" + +void tlb_fill(CPUUniCore32State *env1, target_ulong addr, int is_write, + int mmu_idx, uintptr_t retaddr) +{ + TranslationBlock *tb; + CPUUniCore32State *saved_env; + unsigned long pc; + int ret; + + saved_env = env; + env = env1; + ret = uc32_cpu_handle_mmu_fault(env, addr, is_write, mmu_idx); + if (unlikely(ret)) { + if (retaddr) { + /* now we have a real cpu fault */ + pc = (unsigned long)retaddr; + tb = tb_find_pc(pc); + if (tb) {/* the PC is inside the translated code. + It means that we have a virtual CPU fault */ + cpu_restore_state(tb, env, pc); + } + } + cpu_loop_exit(env); + } + env = saved_env; +} +#endif diff --git a/target-unicore32/softmmu.c b/target-unicore32/softmmu.c new file mode 100644 index 0000000000..373f94b274 --- /dev/null +++ b/target-unicore32/softmmu.c @@ -0,0 +1,267 @@ +/* + * Softmmu related functions + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#ifdef CONFIG_USER_ONLY +#error This file only exist under softmmu circumstance +#endif + +#include <cpu.h> + +#undef DEBUG_UC32 + +#ifdef DEBUG_UC32 +#define DPRINTF(fmt, ...) printf("%s: " fmt , __func__, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define SUPERPAGE_SIZE (1 << 22) +#define UC32_PAGETABLE_READ (1 << 8) +#define UC32_PAGETABLE_WRITE (1 << 7) +#define UC32_PAGETABLE_EXEC (1 << 6) +#define UC32_PAGETABLE_EXIST (1 << 2) +#define PAGETABLE_TYPE(x) ((x) & 3) + + +/* Map CPU modes onto saved register banks. */ +static inline int bank_number(int mode) +{ + switch (mode) { + case ASR_MODE_USER: + case ASR_MODE_SUSR: + return 0; + case ASR_MODE_PRIV: + return 1; + case ASR_MODE_TRAP: + return 2; + case ASR_MODE_EXTN: + return 3; + case ASR_MODE_INTR: + return 4; + } + cpu_abort(cpu_single_env, "Bad mode %x\n", mode); + return -1; +} + +void switch_mode(CPUUniCore32State *env, int mode) +{ + int old_mode; + int i; + + old_mode = env->uncached_asr & ASR_M; + if (mode == old_mode) { + return; + } + + i = bank_number(old_mode); + env->banked_r29[i] = env->regs[29]; + env->banked_r30[i] = env->regs[30]; + env->banked_bsr[i] = env->bsr; + + i = bank_number(mode); + env->regs[29] = env->banked_r29[i]; + env->regs[30] = env->banked_r30[i]; + env->bsr = env->banked_bsr[i]; +} + +/* Handle a CPU exception. */ +void do_interrupt(CPUUniCore32State *env) +{ + uint32_t addr; + int new_mode; + + switch (env->exception_index) { + case UC32_EXCP_PRIV: + new_mode = ASR_MODE_PRIV; + addr = 0x08; + break; + case UC32_EXCP_ITRAP: + DPRINTF("itrap happened at %x\n", env->regs[31]); + new_mode = ASR_MODE_TRAP; + addr = 0x0c; + break; + case UC32_EXCP_DTRAP: + DPRINTF("dtrap happened at %x\n", env->regs[31]); + new_mode = ASR_MODE_TRAP; + addr = 0x10; + break; + case UC32_EXCP_INTR: + new_mode = ASR_MODE_INTR; + addr = 0x18; + break; + default: + cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index); + return; + } + /* High vectors. */ + if (env->cp0.c1_sys & (1 << 13)) { + addr += 0xffff0000; + } + + switch_mode(env, new_mode); + env->bsr = cpu_asr_read(env); + env->uncached_asr = (env->uncached_asr & ~ASR_M) | new_mode; + env->uncached_asr |= ASR_I; + /* The PC already points to the proper instruction. */ + env->regs[30] = env->regs[31]; + env->regs[31] = addr; + env->interrupt_request |= CPU_INTERRUPT_EXITTB; +} + +static int get_phys_addr_ucv2(CPUUniCore32State *env, uint32_t address, + int access_type, int is_user, uint32_t *phys_ptr, int *prot, + target_ulong *page_size) +{ + int code; + uint32_t table; + uint32_t desc; + uint32_t phys_addr; + + /* Pagetable walk. */ + /* Lookup l1 descriptor. */ + table = env->cp0.c2_base & 0xfffff000; + table |= (address >> 20) & 0xffc; + desc = ldl_phys(table); + code = 0; + switch (PAGETABLE_TYPE(desc)) { + case 3: + /* Superpage */ + if (!(desc & UC32_PAGETABLE_EXIST)) { + code = 0x0b; /* superpage miss */ + goto do_fault; + } + phys_addr = (desc & 0xffc00000) | (address & 0x003fffff); + *page_size = SUPERPAGE_SIZE; + break; + case 0: + /* Lookup l2 entry. */ + if (is_user) { + DPRINTF("PGD address %x, desc %x\n", table, desc); + } + if (!(desc & UC32_PAGETABLE_EXIST)) { + code = 0x05; /* second pagetable miss */ + goto do_fault; + } + table = (desc & 0xfffff000) | ((address >> 10) & 0xffc); + desc = ldl_phys(table); + /* 4k page. */ + if (is_user) { + DPRINTF("PTE address %x, desc %x\n", table, desc); + } + if (!(desc & UC32_PAGETABLE_EXIST)) { + code = 0x08; /* page miss */ + goto do_fault; + } + switch (PAGETABLE_TYPE(desc)) { + case 0: + phys_addr = (desc & 0xfffff000) | (address & 0xfff); + *page_size = TARGET_PAGE_SIZE; + break; + default: + cpu_abort(env, "wrong page type!"); + } + break; + default: + cpu_abort(env, "wrong page type!"); + } + + *phys_ptr = phys_addr; + *prot = 0; + /* Check access permissions. */ + if (desc & UC32_PAGETABLE_READ) { + *prot |= PAGE_READ; + } else { + if (is_user && (access_type == 0)) { + code = 0x11; /* access unreadable area */ + goto do_fault; + } + } + + if (desc & UC32_PAGETABLE_WRITE) { + *prot |= PAGE_WRITE; + } else { + if (is_user && (access_type == 1)) { + code = 0x12; /* access unwritable area */ + goto do_fault; + } + } + + if (desc & UC32_PAGETABLE_EXEC) { + *prot |= PAGE_EXEC; + } else { + if (is_user && (access_type == 2)) { + code = 0x13; /* access unexecutable area */ + goto do_fault; + } + } + +do_fault: + return code; +} + +int uc32_cpu_handle_mmu_fault(CPUUniCore32State *env, target_ulong address, + int access_type, int mmu_idx) +{ + uint32_t phys_addr; + target_ulong page_size; + int prot; + int ret, is_user; + + ret = 1; + is_user = mmu_idx == MMU_USER_IDX; + + if ((env->cp0.c1_sys & 1) == 0) { + /* MMU disabled. */ + phys_addr = address; + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + page_size = TARGET_PAGE_SIZE; + ret = 0; + } else { + if ((address & (1 << 31)) || (is_user)) { + ret = get_phys_addr_ucv2(env, address, access_type, is_user, + &phys_addr, &prot, &page_size); + if (is_user) { + DPRINTF("user space access: ret %x, address %x, " + "access_type %x, phys_addr %x, prot %x\n", + ret, address, access_type, phys_addr, prot); + } + } else { + /*IO memory */ + phys_addr = address | (1 << 31); + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + page_size = TARGET_PAGE_SIZE; + ret = 0; + } + } + + if (ret == 0) { + /* Map a single page. */ + phys_addr &= TARGET_PAGE_MASK; + address &= TARGET_PAGE_MASK; + tlb_set_page(env, address, phys_addr, prot, mmu_idx, page_size); + return 0; + } + + env->cp0.c3_faultstatus = ret; + env->cp0.c4_faultaddr = address; + if (access_type == 2) { + env->exception_index = UC32_EXCP_ITRAP; + } else { + env->exception_index = UC32_EXCP_DTRAP; + } + return ret; +} + +target_phys_addr_t cpu_get_phys_page_debug(CPUUniCore32State *env, + target_ulong addr) +{ + cpu_abort(env, "%s not supported yet\n", __func__); + return addr; +} diff --git a/target-unicore32/translate.c b/target-unicore32/translate.c index 9793d14c1b..188bf8c52a 100644 --- a/target-unicore32/translate.c +++ b/target-unicore32/translate.c @@ -1,7 +1,7 @@ /* * UniCore32 translation * - * Copyright (C) 2010-2011 GUAN Xue-tao + * Copyright (C) 2010-2012 Guan Xuetao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -33,9 +33,16 @@ typedef struct DisasContext { int condlabel; struct TranslationBlock *tb; int singlestep_enabled; +#ifndef CONFIG_USER_ONLY + int user; +#endif } DisasContext; -#define IS_USER(s) 1 +#ifndef CONFIG_USER_ONLY +#define IS_USER(s) (s->user) +#else +#define IS_USER(s) 1 +#endif /* These instructions trap after executing, so defer them until after the conditional executions state has been updated. */ @@ -176,6 +183,73 @@ static void store_reg(DisasContext *s, int reg, TCGv var) "Illegal UniCore32 instruction %x at line %d!", \ insn, __LINE__) +#ifndef CONFIG_USER_ONLY +static void disas_cp0_insn(CPUUniCore32State *env, DisasContext *s, + uint32_t insn) +{ + TCGv tmp, tmp2, tmp3; + if ((insn & 0xfe000000) == 0xe0000000) { + tmp2 = new_tmp(); + tmp3 = new_tmp(); + tcg_gen_movi_i32(tmp2, UCOP_REG_N); + tcg_gen_movi_i32(tmp3, UCOP_IMM10); + if (UCOP_SET_L) { + tmp = new_tmp(); + gen_helper_cp0_get(tmp, cpu_env, tmp2, tmp3); + store_reg(s, UCOP_REG_D, tmp); + } else { + tmp = load_reg(s, UCOP_REG_D); + gen_helper_cp0_set(cpu_env, tmp, tmp2, tmp3); + dead_tmp(tmp); + } + dead_tmp(tmp2); + dead_tmp(tmp3); + return; + } + ILLEGAL; +} + +static void disas_ocd_insn(CPUUniCore32State *env, DisasContext *s, + uint32_t insn) +{ + TCGv tmp; + + if ((insn & 0xff003fff) == 0xe1000400) { + /* + * movc rd, pp.nn, #imm9 + * rd: UCOP_REG_D + * nn: UCOP_REG_N (must be 0) + * imm9: 0 + */ + if (UCOP_REG_N == 0) { + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, 0); + store_reg(s, UCOP_REG_D, tmp); + return; + } else { + ILLEGAL; + } + } + if ((insn & 0xff003fff) == 0xe0000401) { + /* + * movc pp.nn, rn, #imm9 + * rn: UCOP_REG_D + * nn: UCOP_REG_N (must be 1) + * imm9: 1 + */ + if (UCOP_REG_N == 1) { + tmp = load_reg(s, UCOP_REG_D); + gen_helper_cp1_putc(tmp); + dead_tmp(tmp); + return; + } else { + ILLEGAL; + } + } + ILLEGAL; +} +#endif + static inline void gen_set_asr(TCGv var, uint32_t mask) { TCGv tmp_mask = tcg_const_i32(mask); @@ -1124,9 +1198,18 @@ static void gen_exception_return(DisasContext *s, TCGv pc) s->is_jmp = DISAS_UPDATE; } -static void disas_coproc_insn(CPUUniCore32State *env, DisasContext *s, uint32_t insn) +static void disas_coproc_insn(CPUUniCore32State *env, DisasContext *s, + uint32_t insn) { switch (UCOP_CPNUM) { +#ifndef CONFIG_USER_ONLY + case 0: + disas_cp0_insn(env, s, insn); + break; + case 1: + disas_ocd_insn(env, s, insn); + break; +#endif case 2: disas_ucf64_insn(env, s, insn); break; @@ -1478,12 +1561,12 @@ static void do_misc(CPUUniCore32State *env, DisasContext *s, uint32_t insn) /* load/store I_offset and R_offset */ static void do_ldst_ir(CPUUniCore32State *env, DisasContext *s, uint32_t insn) { - unsigned int i; + unsigned int mmu_idx; TCGv tmp; TCGv tmp2; tmp2 = load_reg(s, UCOP_REG_N); - i = (IS_USER(s) || (!UCOP_SET_P && UCOP_SET_W)); + mmu_idx = (IS_USER(s) || (!UCOP_SET_P && UCOP_SET_W)); /* immediate */ if (UCOP_SET_P) { @@ -1493,17 +1576,17 @@ static void do_ldst_ir(CPUUniCore32State *env, DisasContext *s, uint32_t insn) if (UCOP_SET_L) { /* load */ if (UCOP_SET_B) { - tmp = gen_ld8u(tmp2, i); + tmp = gen_ld8u(tmp2, mmu_idx); } else { - tmp = gen_ld32(tmp2, i); + tmp = gen_ld32(tmp2, mmu_idx); } } else { /* store */ tmp = load_reg(s, UCOP_REG_D); if (UCOP_SET_B) { - gen_st8(tmp, tmp2, i); + gen_st8(tmp, tmp2, mmu_idx); } else { - gen_st32(tmp, tmp2, i); + gen_st32(tmp, tmp2, mmu_idx); } } if (!UCOP_SET_P) { @@ -1606,7 +1689,7 @@ static void do_ldst_hwsb(CPUUniCore32State *env, DisasContext *s, uint32_t insn) /* load/store multiple words */ static void do_ldst_m(CPUUniCore32State *env, DisasContext *s, uint32_t insn) { - unsigned int val, i; + unsigned int val, i, mmu_idx; int j, n, reg, user, loaded_base; TCGv tmp; TCGv tmp2; @@ -1627,6 +1710,7 @@ static void do_ldst_m(CPUUniCore32State *env, DisasContext *s, uint32_t insn) } } + mmu_idx = (IS_USER(s) || (!UCOP_SET_P && UCOP_SET_W)); addr = load_reg(s, UCOP_REG_N); /* compute total size */ @@ -1671,7 +1755,7 @@ static void do_ldst_m(CPUUniCore32State *env, DisasContext *s, uint32_t insn) } if (UCOP_SET(i)) { if (UCOP_SET_L) { /* load */ - tmp = gen_ld32(addr, IS_USER(s)); + tmp = gen_ld32(addr, mmu_idx); if (reg == 31) { gen_bx(s, tmp); } else if (user) { @@ -1699,7 +1783,7 @@ static void do_ldst_m(CPUUniCore32State *env, DisasContext *s, uint32_t insn) } else { tmp = load_reg(s, reg); } - gen_st32(tmp, addr, IS_USER(s)); + gen_st32(tmp, addr, mmu_idx); } j++; /* no need to add after the last transfer */ @@ -1888,6 +1972,14 @@ static inline void gen_intermediate_code_internal(CPUUniCore32State *env, max_insns = CF_COUNT_MASK; } +#ifndef CONFIG_USER_ONLY + if ((env->uncached_asr & ASR_M) == ASR_MODE_USER) { + dc->user = 1; + } else { + dc->user = 0; + } +#endif + gen_icount_start(); do { if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { @@ -2046,12 +2138,12 @@ static const char *cpu_mode_names[16] = { "UM18", "UM19", "UM1A", "EXTN", "UM1C", "UM1D", "UM1E", "SUSR" }; -#define UCF64_DUMP_STATE -void cpu_dump_state(CPUUniCore32State *env, FILE *f, fprintf_function cpu_fprintf, - int flags) +#undef UCF64_DUMP_STATE +#ifdef UCF64_DUMP_STATE +static void cpu_dump_state_ucf64(CPUUniCore32State *env, FILE *f, + fprintf_function cpu_fprintf, int flags) { int i; -#ifdef UCF64_DUMP_STATE union { uint32_t i; float s; @@ -2063,7 +2155,28 @@ void cpu_dump_state(CPUUniCore32State *env, FILE *f, fprintf_function cpu_fprint float64 f64; double d; } d0; + + for (i = 0; i < 16; i++) { + d.d = env->ucf64.regs[i]; + s0.i = d.l.lower; + s1.i = d.l.upper; + d0.f64 = d.d; + cpu_fprintf(f, "s%02d=%08x(%8g) s%02d=%08x(%8g)", + i * 2, (int)s0.i, s0.s, + i * 2 + 1, (int)s1.i, s1.s); + cpu_fprintf(f, " d%02d=%" PRIx64 "(%8g)\n", + i, (uint64_t)d0.f64, d0.d); + } + cpu_fprintf(f, "FPSCR: %08x\n", (int)env->ucf64.xregs[UC32_UCF64_FPSCR]); +} +#else +#define cpu_dump_state_ucf64(env, file, pr, flags) do { } while (0) #endif + +void cpu_dump_state(CPUUniCore32State *env, FILE *f, + fprintf_function cpu_fprintf, int flags) +{ + int i; uint32_t psr; for (i = 0; i < 32; i++) { @@ -2083,19 +2196,7 @@ void cpu_dump_state(CPUUniCore32State *env, FILE *f, fprintf_function cpu_fprint psr & (1 << 28) ? 'V' : '-', cpu_mode_names[psr & 0xf]); -#ifdef UCF64_DUMP_STATE - for (i = 0; i < 16; i++) { - d.d = env->ucf64.regs[i]; - s0.i = d.l.lower; - s1.i = d.l.upper; - d0.f64 = d.d; - cpu_fprintf(f, "s%02d=%08x(%8g) s%02d=%08x(%8g) d%02d=%" PRIx64 "(%8g)\n", - i * 2, (int)s0.i, s0.s, - i * 2 + 1, (int)s1.i, s1.s, - i, (uint64_t)d0.f64, d0.d); - } - cpu_fprintf(f, "FPSCR: %08x\n", (int)env->ucf64.xregs[UC32_UCF64_FPSCR]); -#endif + cpu_dump_state_ucf64(env, f, cpu_fprintf, flags); } void restore_state_to_opc(CPUUniCore32State *env, TranslationBlock *tb, int pc_pos) diff --git a/target-unicore32/ucf64_helper.c b/target-unicore32/ucf64_helper.c new file mode 100644 index 0000000000..a516edd319 --- /dev/null +++ b/target-unicore32/ucf64_helper.c @@ -0,0 +1,345 @@ +/* + * UniCore-F64 simulation helpers for QEMU. + * + * Copyright (C) 2010-2012 Guan Xuetao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or any later version. + * See the COPYING file in the top-level directory. + */ +#include "cpu.h" +#include "helper.h" + +/* + * The convention used for UniCore-F64 instructions: + * Single precition routines have a "s" suffix + * Double precision routines have a "d" suffix. + */ + +/* Convert host exception flags to f64 form. */ +static inline int ucf64_exceptbits_from_host(int host_bits) +{ + int target_bits = 0; + + if (host_bits & float_flag_invalid) { + target_bits |= UCF64_FPSCR_FLAG_INVALID; + } + if (host_bits & float_flag_divbyzero) { + target_bits |= UCF64_FPSCR_FLAG_DIVZERO; + } + if (host_bits & float_flag_overflow) { + target_bits |= UCF64_FPSCR_FLAG_OVERFLOW; + } + if (host_bits & float_flag_underflow) { + target_bits |= UCF64_FPSCR_FLAG_UNDERFLOW; + } + if (host_bits & float_flag_inexact) { + target_bits |= UCF64_FPSCR_FLAG_INEXACT; + } + return target_bits; +} + +uint32_t HELPER(ucf64_get_fpscr)(CPUUniCore32State *env) +{ + int i; + uint32_t fpscr; + + fpscr = (env->ucf64.xregs[UC32_UCF64_FPSCR] & UCF64_FPSCR_MASK); + i = get_float_exception_flags(&env->ucf64.fp_status); + fpscr |= ucf64_exceptbits_from_host(i); + return fpscr; +} + +/* Convert ucf64 exception flags to target form. */ +static inline int ucf64_exceptbits_to_host(int target_bits) +{ + int host_bits = 0; + + if (target_bits & UCF64_FPSCR_FLAG_INVALID) { + host_bits |= float_flag_invalid; + } + if (target_bits & UCF64_FPSCR_FLAG_DIVZERO) { + host_bits |= float_flag_divbyzero; + } + if (target_bits & UCF64_FPSCR_FLAG_OVERFLOW) { + host_bits |= float_flag_overflow; + } + if (target_bits & UCF64_FPSCR_FLAG_UNDERFLOW) { + host_bits |= float_flag_underflow; + } + if (target_bits & UCF64_FPSCR_FLAG_INEXACT) { + host_bits |= float_flag_inexact; + } + return host_bits; +} + +void HELPER(ucf64_set_fpscr)(CPUUniCore32State *env, uint32_t val) +{ + int i; + uint32_t changed; + + changed = env->ucf64.xregs[UC32_UCF64_FPSCR]; + env->ucf64.xregs[UC32_UCF64_FPSCR] = (val & UCF64_FPSCR_MASK); + + changed ^= val; + if (changed & (UCF64_FPSCR_RND_MASK)) { + i = UCF64_FPSCR_RND(val); + switch (i) { + case 0: + i = float_round_nearest_even; + break; + case 1: + i = float_round_to_zero; + break; + case 2: + i = float_round_up; + break; + case 3: + i = float_round_down; + break; + default: /* 100 and 101 not implement */ + cpu_abort(env, "Unsupported UniCore-F64 round mode"); + } + set_float_rounding_mode(i, &env->ucf64.fp_status); + } + + i = ucf64_exceptbits_to_host(UCF64_FPSCR_TRAPEN(val)); + set_float_exception_flags(i, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_adds)(float32 a, float32 b, CPUUniCore32State *env) +{ + return float32_add(a, b, &env->ucf64.fp_status); +} + +float64 HELPER(ucf64_addd)(float64 a, float64 b, CPUUniCore32State *env) +{ + return float64_add(a, b, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_subs)(float32 a, float32 b, CPUUniCore32State *env) +{ + return float32_sub(a, b, &env->ucf64.fp_status); +} + +float64 HELPER(ucf64_subd)(float64 a, float64 b, CPUUniCore32State *env) +{ + return float64_sub(a, b, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_muls)(float32 a, float32 b, CPUUniCore32State *env) +{ + return float32_mul(a, b, &env->ucf64.fp_status); +} + +float64 HELPER(ucf64_muld)(float64 a, float64 b, CPUUniCore32State *env) +{ + return float64_mul(a, b, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_divs)(float32 a, float32 b, CPUUniCore32State *env) +{ + return float32_div(a, b, &env->ucf64.fp_status); +} + +float64 HELPER(ucf64_divd)(float64 a, float64 b, CPUUniCore32State *env) +{ + return float64_div(a, b, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_negs)(float32 a) +{ + return float32_chs(a); +} + +float64 HELPER(ucf64_negd)(float64 a) +{ + return float64_chs(a); +} + +float32 HELPER(ucf64_abss)(float32 a) +{ + return float32_abs(a); +} + +float64 HELPER(ucf64_absd)(float64 a) +{ + return float64_abs(a); +} + +void HELPER(ucf64_cmps)(float32 a, float32 b, uint32_t c, + CPUUniCore32State *env) +{ + int flag; + flag = float32_compare_quiet(a, b, &env->ucf64.fp_status); + env->CF = 0; + switch (c & 0x7) { + case 0: /* F */ + break; + case 1: /* UN */ + if (flag == 2) { + env->CF = 1; + } + break; + case 2: /* EQ */ + if (flag == 0) { + env->CF = 1; + } + break; + case 3: /* UEQ */ + if ((flag == 0) || (flag == 2)) { + env->CF = 1; + } + break; + case 4: /* OLT */ + if (flag == -1) { + env->CF = 1; + } + break; + case 5: /* ULT */ + if ((flag == -1) || (flag == 2)) { + env->CF = 1; + } + break; + case 6: /* OLE */ + if ((flag == -1) || (flag == 0)) { + env->CF = 1; + } + break; + case 7: /* ULE */ + if (flag != 1) { + env->CF = 1; + } + break; + } + env->ucf64.xregs[UC32_UCF64_FPSCR] = (env->CF << 29) + | (env->ucf64.xregs[UC32_UCF64_FPSCR] & 0x0fffffff); +} + +void HELPER(ucf64_cmpd)(float64 a, float64 b, uint32_t c, + CPUUniCore32State *env) +{ + int flag; + flag = float64_compare_quiet(a, b, &env->ucf64.fp_status); + env->CF = 0; + switch (c & 0x7) { + case 0: /* F */ + break; + case 1: /* UN */ + if (flag == 2) { + env->CF = 1; + } + break; + case 2: /* EQ */ + if (flag == 0) { + env->CF = 1; + } + break; + case 3: /* UEQ */ + if ((flag == 0) || (flag == 2)) { + env->CF = 1; + } + break; + case 4: /* OLT */ + if (flag == -1) { + env->CF = 1; + } + break; + case 5: /* ULT */ + if ((flag == -1) || (flag == 2)) { + env->CF = 1; + } + break; + case 6: /* OLE */ + if ((flag == -1) || (flag == 0)) { + env->CF = 1; + } + break; + case 7: /* ULE */ + if (flag != 1) { + env->CF = 1; + } + break; + } + env->ucf64.xregs[UC32_UCF64_FPSCR] = (env->CF << 29) + | (env->ucf64.xregs[UC32_UCF64_FPSCR] & 0x0fffffff); +} + +/* Helper routines to perform bitwise copies between float and int. */ +static inline float32 ucf64_itos(uint32_t i) +{ + union { + uint32_t i; + float32 s; + } v; + + v.i = i; + return v.s; +} + +static inline uint32_t ucf64_stoi(float32 s) +{ + union { + uint32_t i; + float32 s; + } v; + + v.s = s; + return v.i; +} + +static inline float64 ucf64_itod(uint64_t i) +{ + union { + uint64_t i; + float64 d; + } v; + + v.i = i; + return v.d; +} + +static inline uint64_t ucf64_dtoi(float64 d) +{ + union { + uint64_t i; + float64 d; + } v; + + v.d = d; + return v.i; +} + +/* Integer to float conversion. */ +float32 HELPER(ucf64_si2sf)(float32 x, CPUUniCore32State *env) +{ + return int32_to_float32(ucf64_stoi(x), &env->ucf64.fp_status); +} + +float64 HELPER(ucf64_si2df)(float32 x, CPUUniCore32State *env) +{ + return int32_to_float64(ucf64_stoi(x), &env->ucf64.fp_status); +} + +/* Float to integer conversion. */ +float32 HELPER(ucf64_sf2si)(float32 x, CPUUniCore32State *env) +{ + return ucf64_itos(float32_to_int32(x, &env->ucf64.fp_status)); +} + +float32 HELPER(ucf64_df2si)(float64 x, CPUUniCore32State *env) +{ + return ucf64_itos(float64_to_int32(x, &env->ucf64.fp_status)); +} + +/* floating point conversion */ +float64 HELPER(ucf64_sf2df)(float32 x, CPUUniCore32State *env) +{ + return float32_to_float64(x, &env->ucf64.fp_status); +} + +float32 HELPER(ucf64_df2sf)(float64 x, CPUUniCore32State *env) +{ + return float64_to_float32(x, &env->ucf64.fp_status); +} diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h index f7db116400..177094ae9a 100644 --- a/target-xtensa/cpu.h +++ b/target-xtensa/cpu.h @@ -351,6 +351,12 @@ typedef struct CPUXtensaState { #define cpu_signal_handler cpu_xtensa_signal_handler #define cpu_list xtensa_cpu_list +#ifdef TARGET_WORDS_BIGENDIAN +#define XTENSA_DEFAULT_CPU_MODEL "fsf" +#else +#define XTENSA_DEFAULT_CPU_MODEL "dc232b" +#endif + XtensaCPU *cpu_xtensa_init(const char *cpu_model); static inline CPUXtensaState *cpu_init(const char *cpu_model) diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c index 044ce18364..d5bb171fcd 100644 --- a/target-xtensa/helper.c +++ b/target-xtensa/helper.c @@ -54,8 +54,6 @@ static uint32_t check_hw_breakpoints(CPUXtensaState *env) return 0; } -static CPUDebugExcpHandler *prev_debug_excp_handler; - static void breakpoint_handler(CPUXtensaState *env) { if (env->watchpoint_hit) { @@ -70,9 +68,6 @@ static void breakpoint_handler(CPUXtensaState *env) cpu_resume_from_signal(env, NULL); } } - if (prev_debug_excp_handler) { - prev_debug_excp_handler(env); - } } XtensaCPU *cpu_xtensa_init(const char *cpu_model) @@ -105,8 +100,7 @@ XtensaCPU *cpu_xtensa_init(const char *cpu_model) if (!debug_handler_inited && tcg_enabled()) { debug_handler_inited = 1; - prev_debug_excp_handler = - cpu_set_debug_excp_handler(breakpoint_handler); + cpu_set_debug_excp_handler(breakpoint_handler); } xtensa_irq_init(env); diff --git a/tests/qemu-iotests/031.out b/tests/qemu-iotests/031.out index d3cab301d4..796c993df2 100644 --- a/tests/qemu-iotests/031.out +++ b/tests/qemu-iotests/031.out @@ -54,8 +54,8 @@ header_length 72 Header extension: magic 0x6803f857 -length 0 -data '' +length 96 +data <binary> Header extension: magic 0x12345678 @@ -68,7 +68,7 @@ No errors were found on the image. magic 0x514649fb version 2 -backing_file_offset 0x98 +backing_file_offset 0xf8 backing_file_size 0x17 cluster_bits 16 size 67108864 @@ -92,8 +92,8 @@ data 'host_device' Header extension: magic 0x6803f857 -length 0 -data '' +length 96 +data <binary> Header extension: magic 0x12345678 @@ -155,8 +155,8 @@ header_length 104 Header extension: magic 0x6803f857 -length 0 -data '' +length 96 +data <binary> Header extension: magic 0x12345678 @@ -169,7 +169,7 @@ No errors were found on the image. magic 0x514649fb version 3 -backing_file_offset 0xb8 +backing_file_offset 0x118 backing_file_size 0x17 cluster_bits 16 size 67108864 @@ -193,8 +193,8 @@ data 'host_device' Header extension: magic 0x6803f857 -length 0 -data '' +length 96 +data <binary> Header extension: magic 0x12345678 diff --git a/tests/qemu-iotests/036.out b/tests/qemu-iotests/036.out index 6953e37ab6..063ca22d66 100644 --- a/tests/qemu-iotests/036.out +++ b/tests/qemu-iotests/036.out @@ -46,7 +46,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 0 -data '' +length 96 +data <binary> *** done diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 new file mode 100755 index 0000000000..c5ae806ecb --- /dev/null +++ b/tests/qemu-iotests/039 @@ -0,0 +1,137 @@ +#!/bin/bash +# +# Test qcow2 lazy refcounts +# +# Copyright (C) 2012 Red Hat, Inc. +# Copyright IBM, Corp. 2010 +# +# Based on test 038. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=stefanha@linux.vnet.ibm.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux +_unsupported_qemu_io_options --nocache + +size=128M + +echo +echo "== Checking that image is clean on shutdown ==" + +IMGOPTS="compat=1.1,lazy_refcounts=on" +_make_test_img $size + +$QEMU_IO -c "write -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io + +# The dirty bit must not be set +./qcow2.py $TEST_IMG dump-header | grep incompatible_features +_check_test_img + +echo +echo "== Creating a dirty image file ==" + +IMGOPTS="compat=1.1,lazy_refcounts=on" +_make_test_img $size + +old_ulimit=$(ulimit -c) +ulimit -c 0 # do not produce a core dump on abort(3) +$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" $TEST_IMG | _filter_qemu_io +ulimit -c "$old_ulimit" + +# The dirty bit must be set +./qcow2.py $TEST_IMG dump-header | grep incompatible_features +_check_test_img + +echo +echo "== Read-only access must still work ==" + +$QEMU_IO -r -c "read -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io + +# The dirty bit must be set +./qcow2.py $TEST_IMG dump-header | grep incompatible_features + +echo +echo "== Repairing the image file must succeed ==" + +$QEMU_IMG check -r all $TEST_IMG + +# The dirty bit must not be set +./qcow2.py $TEST_IMG dump-header | grep incompatible_features + +echo +echo "== Data should still be accessible after repair ==" + +$QEMU_IO -c "read -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io + +echo +echo "== Opening a dirty image read/write should repair it ==" + +IMGOPTS="compat=1.1,lazy_refcounts=on" +_make_test_img $size + +old_ulimit=$(ulimit -c) +ulimit -c 0 # do not produce a core dump on abort(3) +$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" $TEST_IMG | _filter_qemu_io +ulimit -c "$old_ulimit" + +# The dirty bit must be set +./qcow2.py $TEST_IMG dump-header | grep incompatible_features + +$QEMU_IO -c "write 0 512" $TEST_IMG | _filter_qemu_io + +# The dirty bit must not be set +./qcow2.py $TEST_IMG dump-header | grep incompatible_features + +echo +echo "== Creating an image file with lazy_refcounts=off ==" + +IMGOPTS="compat=1.1,lazy_refcounts=off" +_make_test_img $size + +old_ulimit=$(ulimit -c) +ulimit -c 0 # do not produce a core dump on abort(3) +$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" $TEST_IMG | _filter_qemu_io +ulimit -c "$old_ulimit" + +# The dirty bit must not be set since lazy_refcounts=off +./qcow2.py $TEST_IMG dump-header | grep incompatible_features +_check_test_img + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 + diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out new file mode 100644 index 0000000000..cb510d6716 --- /dev/null +++ b/tests/qemu-iotests/039.out @@ -0,0 +1,59 @@ +QA output created by 039 + +== Checking that image is clean on shutdown == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +incompatible_features 0x0 +No errors were found on the image. + +== Creating a dirty image file == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +incompatible_features 0x1 +ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0 +ERROR cluster 5 refcount=0 reference=1 + +2 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. + +== Read-only access must still work == +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +incompatible_features 0x1 + +== Repairing the image file must succeed == +ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0 +Repairing cluster 5 refcount=0 reference=1 +The following inconsistencies were found and repaired: + + 0 leaked clusters + 1 corruptions + +Double checking the fixed image now... +No errors were found on the image. +incompatible_features 0x0 + +== Data should still be accessible after repair == +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== Opening a dirty image read/write should repair it == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +incompatible_features 0x1 +ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0 +Repairing cluster 5 refcount=0 reference=1 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +incompatible_features 0x0 + +== Creating an image file with lazy_refcounts=off == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +incompatible_features 0x0 +No errors were found on the image. +*** done diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 5e3a524bc8..d534e9466d 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -105,15 +105,16 @@ _make_test_img() # XXX(hch): have global image options? $QEMU_IMG create -f $IMGFMT $extra_img_options $TEST_IMG $image_size | \ - sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" | \ - sed -e "s#$TEST_DIR#TEST_DIR#g" | \ - sed -e "s#$IMGFMT#IMGFMT#g" | \ - sed -e "s# encryption=off##g" | \ - sed -e "s# cluster_size=[0-9]\\+##g" | \ - sed -e "s# table_size=0##g" | \ - sed -e "s# compat='[^']*'##g" | \ - sed -e "s# compat6=off##g" | \ - sed -e "s# static=off##g" + sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ + -e "s#$TEST_DIR#TEST_DIR#g" \ + -e "s#$IMGFMT#IMGFMT#g" \ + -e "s# encryption=off##g" \ + -e "s# cluster_size=[0-9]\\+##g" \ + -e "s# table_size=[0-9]\\+##g" \ + -e "s# compat='[^']*'##g" \ + -e "s# compat6=\\(on\\|off\\)##g" \ + -e "s# static=\\(on\\|off\\)##g" \ + -e "s# lazy_refcounts=\\(on\\|off\\)##g" } _cleanup_test_img() @@ -296,6 +297,20 @@ _supported_os() _notrun "not suitable for this OS: $HOSTOS" } +_unsupported_qemu_io_options() +{ + for bad_opt + do + for opt in $QEMU_IO_OPTIONS + do + if [ "$bad_opt" = "$opt" ] + then + _notrun "not suitable for qemu-io option: $bad_opt" + fi + done + done +} + # this test requires that a specified command (executable) exists # _require_command() diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 7a2c92b6e9..ebb5ca4b41 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -45,3 +45,4 @@ 036 rw auto quick 037 rw auto backing 038 rw auto backing +039 rw auto diff --git a/tests/qemu-iotests/qed.py b/tests/qemu-iotests/qed.py new file mode 100755 index 0000000000..52ff845590 --- /dev/null +++ b/tests/qemu-iotests/qed.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python +# +# Tool to manipulate QED image files +# +# Copyright (C) 2010 IBM, Corp. +# +# Authors: +# Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +import sys +import struct +import random +import optparse + +# This can be used as a module +__all__ = ['QED_F_NEED_CHECK', 'QED'] + +QED_F_NEED_CHECK = 0x02 + +header_fmt = '<IIIIQQQQQII' +header_size = struct.calcsize(header_fmt) +field_names = ['magic', 'cluster_size', 'table_size', + 'header_size', 'features', 'compat_features', + 'autoclear_features', 'l1_table_offset', 'image_size', + 'backing_filename_offset', 'backing_filename_size'] +table_elem_fmt = '<Q' +table_elem_size = struct.calcsize(table_elem_fmt) + +def err(msg): + sys.stderr.write(msg + '\n') + sys.exit(1) + +def unpack_header(s): + fields = struct.unpack(header_fmt, s) + return dict((field_names[idx], val) for idx, val in enumerate(fields)) + +def pack_header(header): + fields = tuple(header[x] for x in field_names) + return struct.pack(header_fmt, *fields) + +def unpack_table_elem(s): + return struct.unpack(table_elem_fmt, s)[0] + +def pack_table_elem(elem): + return struct.pack(table_elem_fmt, elem) + +class QED(object): + def __init__(self, f): + self.f = f + + self.f.seek(0, 2) + self.filesize = f.tell() + + self.load_header() + self.load_l1_table() + + def raw_pread(self, offset, size): + self.f.seek(offset) + return self.f.read(size) + + def raw_pwrite(self, offset, data): + self.f.seek(offset) + return self.f.write(data) + + def load_header(self): + self.header = unpack_header(self.raw_pread(0, header_size)) + + def store_header(self): + self.raw_pwrite(0, pack_header(self.header)) + + def read_table(self, offset): + size = self.header['table_size'] * self.header['cluster_size'] + s = self.raw_pread(offset, size) + table = [unpack_table_elem(s[i:i + table_elem_size]) for i in xrange(0, size, table_elem_size)] + return table + + def load_l1_table(self): + self.l1_table = self.read_table(self.header['l1_table_offset']) + self.table_nelems = self.header['table_size'] * self.header['cluster_size'] / table_elem_size + + def write_table(self, offset, table): + s = ''.join(pack_table_elem(x) for x in table) + self.raw_pwrite(offset, s) + +def random_table_item(table): + vals = [(index, offset) for index, offset in enumerate(table) if offset != 0] + if not vals: + err('cannot pick random item because table is empty') + return random.choice(vals) + +def corrupt_table_duplicate(table): + '''Corrupt a table by introducing a duplicate offset''' + victim_idx, victim_val = random_table_item(table) + unique_vals = set(table) + if len(unique_vals) == 1: + err('no duplication corruption possible in table') + dup_val = random.choice(list(unique_vals.difference([victim_val]))) + table[victim_idx] = dup_val + +def corrupt_table_invalidate(qed, table): + '''Corrupt a table by introducing an invalid offset''' + index, _ = random_table_item(table) + table[index] = qed.filesize + random.randint(0, 100 * 1024 * 1024 * 1024 * 1024) + +def cmd_show(qed, *args): + '''show [header|l1|l2 <offset>]- Show header or l1/l2 tables''' + if not args or args[0] == 'header': + print qed.header + elif args[0] == 'l1': + print qed.l1_table + elif len(args) == 2 and args[0] == 'l2': + offset = int(args[1]) + print qed.read_table(offset) + else: + err('unrecognized sub-command') + +def cmd_duplicate(qed, table_level): + '''duplicate l1|l2 - Duplicate a random table element''' + if table_level == 'l1': + offset = qed.header['l1_table_offset'] + table = qed.l1_table + elif table_level == 'l2': + _, offset = random_table_item(qed.l1_table) + table = qed.read_table(offset) + else: + err('unrecognized sub-command') + corrupt_table_duplicate(table) + qed.write_table(offset, table) + +def cmd_invalidate(qed, table_level): + '''invalidate l1|l2 - Plant an invalid table element at random''' + if table_level == 'l1': + offset = qed.header['l1_table_offset'] + table = qed.l1_table + elif table_level == 'l2': + _, offset = random_table_item(qed.l1_table) + table = qed.read_table(offset) + else: + err('unrecognized sub-command') + corrupt_table_invalidate(qed, table) + qed.write_table(offset, table) + +def cmd_need_check(qed, *args): + '''need-check [on|off] - Test, set, or clear the QED_F_NEED_CHECK header bit''' + if not args: + print bool(qed.header['features'] & QED_F_NEED_CHECK) + return + + if args[0] == 'on': + qed.header['features'] |= QED_F_NEED_CHECK + elif args[0] == 'off': + qed.header['features'] &= ~QED_F_NEED_CHECK + else: + err('unrecognized sub-command') + qed.store_header() + +def cmd_zero_cluster(qed, pos, *args): + '''zero-cluster <pos> [<n>] - Zero data clusters''' + pos, n = int(pos), 1 + if args: + if len(args) != 1: + err('expected one argument') + n = int(args[0]) + + for i in xrange(n): + l1_index = pos / qed.header['cluster_size'] / len(qed.l1_table) + if qed.l1_table[l1_index] == 0: + err('no l2 table allocated') + + l2_offset = qed.l1_table[l1_index] + l2_table = qed.read_table(l2_offset) + + l2_index = (pos / qed.header['cluster_size']) % len(qed.l1_table) + l2_table[l2_index] = 1 # zero the data cluster + qed.write_table(l2_offset, l2_table) + pos += qed.header['cluster_size'] + +def cmd_copy_metadata(qed, outfile): + '''copy-metadata <outfile> - Copy metadata only (for scrubbing corrupted images)''' + out = open(outfile, 'wb') + + # Match file size + out.seek(qed.filesize - 1) + out.write('\0') + + # Copy header clusters + out.seek(0) + header_size_bytes = qed.header['header_size'] * qed.header['cluster_size'] + out.write(qed.raw_pread(0, header_size_bytes)) + + # Copy L1 table + out.seek(qed.header['l1_table_offset']) + s = ''.join(pack_table_elem(x) for x in qed.l1_table) + out.write(s) + + # Copy L2 tables + for l2_offset in qed.l1_table: + if l2_offset == 0: + continue + l2_table = qed.read_table(l2_offset) + out.seek(l2_offset) + s = ''.join(pack_table_elem(x) for x in l2_table) + out.write(s) + + out.close() + +def usage(): + print 'Usage: %s <file> <cmd> [<arg>, ...]' % sys.argv[0] + print + print 'Supported commands:' + for cmd in sorted(x for x in globals() if x.startswith('cmd_')): + print globals()[cmd].__doc__ + sys.exit(1) + +def main(): + if len(sys.argv) < 3: + usage() + filename, cmd = sys.argv[1:3] + + cmd = 'cmd_' + cmd.replace('-', '_') + if cmd not in globals(): + usage() + + qed = QED(open(filename, 'r+b')) + try: + globals()[cmd](qed, *sys.argv[3:]) + except TypeError, e: + sys.stderr.write(globals()[cmd].__doc__ + '\n') + sys.exit(1) + +if __name__ == '__main__': + main() diff --git a/ui/vnc.c b/ui/vnc.c index 312ad7fe36..385e345c31 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3061,7 +3061,7 @@ int vnc_display_open(DisplayState *ds, const char *display) if (strncmp(display, "unix:", 5) == 0) vs->lsock = unix_connect(display+5); else - vs->lsock = inet_connect(display, true, NULL); + vs->lsock = inet_connect(display, true, NULL, NULL); if (-1 == vs->lsock) { g_free(vs->display); vs->display = NULL; diff --git a/user-exec.c b/user-exec.c index 1a9c276eb3..b9ea9dd32f 100644 --- a/user-exec.c +++ b/user-exec.c @@ -18,7 +18,9 @@ */ #include "config.h" #include "cpu.h" +#ifndef CONFIG_TCG_PASS_AREG0 #include "dyngen-exec.h" +#endif #include "disas.h" #include "tcg.h" @@ -58,9 +60,11 @@ void cpu_resume_from_signal(CPUArchState *env1, void *puc) struct sigcontext *uc = puc; #endif +#ifndef CONFIG_TCG_PASS_AREG0 env = env1; /* XXX: restore cpu registers saved in host registers */ +#endif if (puc) { /* XXX: use siglongjmp ? */ @@ -74,8 +78,8 @@ void cpu_resume_from_signal(CPUArchState *env1, void *puc) sigprocmask(SIG_SETMASK, &uc->sc_mask, NULL); #endif } - env->exception_index = -1; - longjmp(env->jmp_env, 1); + env1->exception_index = -1; + longjmp(env1->jmp_env, 1); } /* 'pc' is the host PC at which the exception was raised. 'address' is @@ -89,9 +93,11 @@ static inline int handle_cpu_signal(uintptr_t pc, unsigned long address, TranslationBlock *tb; int ret; +#ifndef CONFIG_TCG_PASS_AREG0 if (cpu_single_env) { env = cpu_single_env; /* XXX: find a correct solution for multithread */ } +#endif #if defined(DEBUG_SIGNAL) qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n", pc, address, is_write, *(unsigned long *)old_set); @@ -103,7 +109,8 @@ static inline int handle_cpu_signal(uintptr_t pc, unsigned long address, } /* see if it is an MMU fault */ - ret = cpu_handle_mmu_fault(env, address, is_write, MMU_USER_IDX); + ret = cpu_handle_mmu_fault(cpu_single_env, address, is_write, + MMU_USER_IDX); if (ret < 0) { return 0; /* not an MMU fault */ } @@ -115,13 +122,13 @@ static inline int handle_cpu_signal(uintptr_t pc, unsigned long address, if (tb) { /* the PC is inside the translated code. It means that we have a virtual CPU fault */ - cpu_restore_state(tb, env, pc); + cpu_restore_state(tb, cpu_single_env, pc); } /* we restore the process signal mask as the sigreturn should do it (XXX: use sigsetjmp) */ sigprocmask(SIG_SETMASK, old_set, NULL); - exception_action(env); + exception_action(cpu_single_env); /* never comes here */ return 1; diff --git a/vl.c b/vl.c index e71cb30ecf..d01256a6a3 100644 --- a/vl.c +++ b/vl.c @@ -293,6 +293,11 @@ static struct { { .driver = "qxl-vga", .flag = &default_vga }, }; +const char *qemu_get_vm_name(void) +{ + return qemu_name; +} + static void res_free(void) { if (boot_splash_filedata != NULL) { @@ -1208,6 +1213,37 @@ QEMUMachine *find_default_machine(void) return NULL; } +MachineInfoList *qmp_query_machines(Error **errp) +{ + MachineInfoList *mach_list = NULL; + QEMUMachine *m; + + for (m = first_machine; m; m = m->next) { + MachineInfoList *entry; + MachineInfo *info; + + info = g_malloc0(sizeof(*info)); + if (m->is_default) { + info->has_is_default = true; + info->is_default = true; + } + + if (m->alias) { + info->has_alias = true; + info->alias = g_strdup(m->alias); + } + + info->name = g_strdup(m->name); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = mach_list; + mach_list = entry; + } + + return mach_list; +} + /***********************************************************/ /* main execution loop */ @@ -1293,6 +1329,7 @@ static pid_t shutdown_pid; static int powerdown_requested; static int debug_requested; static int suspend_requested; +static int wakeup_requested; static NotifierList suspend_notifiers = NOTIFIER_LIST_INITIALIZER(suspend_notifiers); static NotifierList wakeup_notifiers = @@ -1347,6 +1384,13 @@ static int qemu_suspend_requested(void) return r; } +static int qemu_wakeup_requested(void) +{ + int r = wakeup_requested; + wakeup_requested = 0; + return r; +} + int qemu_powerdown_requested(void) { int r = powerdown_requested; @@ -1452,9 +1496,8 @@ void qemu_system_wakeup_request(WakeupReason reason) return; } runstate_set(RUN_STATE_RUNNING); - monitor_protocol_event(QEVENT_WAKEUP, NULL); notifier_list_notify(&wakeup_notifiers, &reason); - reset_requested = 1; + wakeup_requested = 1; qemu_notify_event(); } @@ -1534,6 +1577,13 @@ static bool main_loop_should_exit(void) runstate_set(RUN_STATE_PAUSED); } } + if (qemu_wakeup_requested()) { + pause_all_vcpus(); + cpu_synchronize_all_states(); + qemu_system_reset(VMRESET_SILENT); + resume_all_vcpus(); + monitor_protocol_event(QEVENT_WAKEUP, NULL); + } if (qemu_powerdown_requested()) { monitor_protocol_event(QEVENT_POWERDOWN, NULL); qemu_irq_raise(qemu_system_powerdown); @@ -3204,6 +3254,11 @@ int main(int argc, char **argv, char **envp) } loc_set_none(); + if (machine == NULL) { + fprintf(stderr, "No machine found.\n"); + exit(1); + } + if (machine->hw_version) { qemu_set_version(machine->hw_version); } @@ -3246,11 +3301,6 @@ int main(int argc, char **argv, char **envp) data_dir = CONFIG_QEMU_DATADIR; } - if (machine == NULL) { - fprintf(stderr, "No machine found.\n"); - exit(1); - } - /* * Default to max_cpus = smp_cpus, in case the user doesn't * specify a max_cpus value. @@ -3345,6 +3395,11 @@ int main(int argc, char **argv, char **envp) ram_size = DEFAULT_RAM_SIZE * 1024 * 1024; } + if (qemu_opts_foreach(qemu_find_opts("device"), device_help_func, NULL, 0) + != 0) { + exit(0); + } + configure_accelerator(); qemu_init_cpu_loop(); @@ -3500,9 +3555,6 @@ int main(int argc, char **argv, char **envp) } select_vgahw(vga_model); - if (qemu_opts_foreach(qemu_find_opts("device"), device_help_func, NULL, 0) != 0) - exit(0); - if (watchdog) { i = select_watchdog(watchdog); if (i > 0) |