diff options
215 files changed, 8032 insertions, 6894 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 4ed8215..32c7ca4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -356,6 +356,13 @@ F: hw/misc/zynq_slcr.c F: hw/*/cadence_* F: hw/ssi/xilinx_spips.c +ARM ACPI Subsystem +M: Shannon Zhao <zhaoshenglong@huawei.com> +M: Shannon Zhao <shannon.zhao@linaro.org> +S: Maintained +F: hw/arm/virt-acpi-build.c +F: include/hw/arm/virt-acpi-build.h + CRIS Machines ------------- Axis Dev88 @@ -770,7 +777,6 @@ F: hw/net/rocker/ Subsystems ---------- Audio -M: Vassili Karpov (malc) <av1474@comtv.ru> M: Gerd Hoffmann <kraxel@redhat.com> S: Maintained F: audio/ @@ -1014,8 +1020,6 @@ M: Amit Shah <amit.shah@redhat.com> S: Maintained F: include/migration/ F: migration/ -F: savevm.c -F: arch_init.c F: scripts/vmstate-static-checker.py F: tests/vmstate-static-checker-data/ diff --git a/Makefile.target b/Makefile.target index ec5b92c..3e7aafd 100644 --- a/Makefile.target +++ b/Makefile.target @@ -132,9 +132,10 @@ obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o obj-y += qtest.o bootdevice.o obj-y += hw/ obj-$(CONFIG_KVM) += kvm-all.o -obj-y += memory.o savevm.o cputlb.o +obj-y += memory.o cputlb.o obj-y += memory_mapping.o obj-y += dump.o +obj-y += migration/ram.o migration/savevm.o LIBS := $(libs_softmmu) $(LIBS) # xen support diff --git a/arch_init.c b/arch_init.c index d294474..725c638 100644 --- a/arch_init.c +++ b/arch_init.c @@ -22,46 +22,15 @@ * THE SOFTWARE. */ #include <stdint.h> -#include <stdarg.h> -#include <stdlib.h> -#include <zlib.h> -#ifndef _WIN32 -#include <sys/types.h> -#include <sys/mman.h> -#endif -#include "config.h" -#include "monitor/monitor.h" #include "sysemu/sysemu.h" -#include "qemu/bitops.h" -#include "qemu/bitmap.h" #include "sysemu/arch_init.h" -#include "audio/audio.h" -#include "hw/i386/pc.h" #include "hw/pci/pci.h" #include "hw/audio/audio.h" -#include "sysemu/kvm.h" -#include "migration/migration.h" #include "hw/i386/smbios.h" -#include "exec/address-spaces.h" -#include "hw/audio/pcspk.h" -#include "migration/page_cache.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qmp-commands.h" -#include "trace.h" -#include "exec/cpu-all.h" -#include "exec/ram_addr.h" #include "hw/acpi/acpi.h" -#include "qemu/host-utils.h" -#include "qemu/rcu_queue.h" - -#ifdef DEBUG_ARCH_INIT -#define DPRINTF(fmt, ...) \ - do { fprintf(stdout, "arch_init: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif #ifdef TARGET_SPARC int graphic_width = 1024; @@ -111,24 +80,6 @@ int graphic_depth = 32; #endif const uint32_t arch_type = QEMU_ARCH; -static bool mig_throttle_on; -static int dirty_rate_high_cnt; -static void check_guest_throttling(void); - -static uint64_t bitmap_sync_count; - -/***********************************************************/ -/* ram save/restore */ - -#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */ -#define RAM_SAVE_FLAG_COMPRESS 0x02 -#define RAM_SAVE_FLAG_MEM_SIZE 0x04 -#define RAM_SAVE_FLAG_PAGE 0x08 -#define RAM_SAVE_FLAG_EOS 0x10 -#define RAM_SAVE_FLAG_CONTINUE 0x20 -#define RAM_SAVE_FLAG_XBZRLE 0x40 -/* 0x80 is reserved in migration.h start with 0x100 next */ -#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100 static struct defconfig_file { const char *filename; @@ -139,8 +90,6 @@ static struct defconfig_file { { NULL }, /* end of list */ }; -static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE]; - int qemu_read_default_config_files(bool userconfig) { int ret; @@ -159,1517 +108,6 @@ int qemu_read_default_config_files(bool userconfig) return 0; } -static inline bool is_zero_range(uint8_t *p, uint64_t size) -{ - return buffer_find_nonzero_offset(p, size) == size; -} - -/* 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; - /* Cache for XBZRLE, Protected by lock. */ - PageCache *cache; - QemuMutex lock; -} XBZRLE; - -/* buffer used for XBZRLE decoding */ -static uint8_t *xbzrle_decoded_buf; - -static void XBZRLE_cache_lock(void) -{ - if (migrate_use_xbzrle()) - qemu_mutex_lock(&XBZRLE.lock); -} - -static void XBZRLE_cache_unlock(void) -{ - if (migrate_use_xbzrle()) - qemu_mutex_unlock(&XBZRLE.lock); -} - -/* - * called from qmp_migrate_set_cache_size in main thread, possibly while - * a migration is in progress. - * A running migration maybe using the cache and might finish during this - * call, hence changes to the cache are protected by XBZRLE.lock(). - */ -int64_t xbzrle_cache_resize(int64_t new_size) -{ - PageCache *new_cache; - int64_t ret; - - if (new_size < TARGET_PAGE_SIZE) { - return -1; - } - - XBZRLE_cache_lock(); - - if (XBZRLE.cache != NULL) { - if (pow2floor(new_size) == migrate_xbzrle_cache_size()) { - goto out_new_size; - } - new_cache = cache_init(new_size / TARGET_PAGE_SIZE, - TARGET_PAGE_SIZE); - if (!new_cache) { - error_report("Error creating cache"); - ret = -1; - goto out; - } - - cache_fini(XBZRLE.cache); - XBZRLE.cache = new_cache; - } - -out_new_size: - ret = pow2floor(new_size); -out: - XBZRLE_cache_unlock(); - return ret; -} - -/* accounting for migration statistics */ -typedef struct AccountingInfo { - uint64_t dup_pages; - uint64_t skipped_pages; - uint64_t norm_pages; - uint64_t iterations; - uint64_t xbzrle_bytes; - uint64_t xbzrle_pages; - uint64_t xbzrle_cache_miss; - double xbzrle_cache_miss_rate; - 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 skipped_mig_bytes_transferred(void) -{ - return acct_info.skipped_pages * TARGET_PAGE_SIZE; -} - -uint64_t skipped_mig_pages_transferred(void) -{ - return acct_info.skipped_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; -} - -double xbzrle_mig_cache_miss_rate(void) -{ - return acct_info.xbzrle_cache_miss_rate; -} - -uint64_t xbzrle_mig_pages_overflow(void) -{ - return acct_info.xbzrle_overflows; -} - -/* This is the last block that we have visited serching for dirty pages - */ -static RAMBlock *last_seen_block; -/* This is the last block from where we have sent data */ -static RAMBlock *last_sent_block; -static ram_addr_t last_offset; -static unsigned long *migration_bitmap; -static uint64_t migration_dirty_pages; -static uint32_t last_version; -static bool ram_bulk_stage; - -struct CompressParam { - bool start; - bool done; - QEMUFile *file; - QemuMutex mutex; - QemuCond cond; - RAMBlock *block; - ram_addr_t offset; -}; -typedef struct CompressParam CompressParam; - -struct DecompressParam { - bool start; - QemuMutex mutex; - QemuCond cond; - void *des; - uint8 *compbuf; - int len; -}; -typedef struct DecompressParam DecompressParam; - -static CompressParam *comp_param; -static QemuThread *compress_threads; -/* comp_done_cond is used to wake up the migration thread when - * one of the compression threads has finished the compression. - * comp_done_lock is used to co-work with comp_done_cond. - */ -static QemuMutex *comp_done_lock; -static QemuCond *comp_done_cond; -/* The empty QEMUFileOps will be used by file in CompressParam */ -static const QEMUFileOps empty_ops = { }; - -static bool compression_switch; -static bool quit_comp_thread; -static bool quit_decomp_thread; -static DecompressParam *decomp_param; -static QemuThread *decompress_threads; -static uint8_t *compressed_data_buf; - -static int do_compress_ram_page(CompressParam *param); - -static void *do_data_compress(void *opaque) -{ - CompressParam *param = opaque; - - while (!quit_comp_thread) { - qemu_mutex_lock(¶m->mutex); - /* Re-check the quit_comp_thread in case of - * terminate_compression_threads is called just before - * qemu_mutex_lock(¶m->mutex) and after - * while(!quit_comp_thread), re-check it here can make - * sure the compression thread terminate as expected. - */ - while (!param->start && !quit_comp_thread) { - qemu_cond_wait(¶m->cond, ¶m->mutex); - } - if (!quit_comp_thread) { - do_compress_ram_page(param); - } - param->start = false; - qemu_mutex_unlock(¶m->mutex); - - qemu_mutex_lock(comp_done_lock); - param->done = true; - qemu_cond_signal(comp_done_cond); - qemu_mutex_unlock(comp_done_lock); - } - - return NULL; -} - -static inline void terminate_compression_threads(void) -{ - int idx, thread_count; - - thread_count = migrate_compress_threads(); - quit_comp_thread = true; - for (idx = 0; idx < thread_count; idx++) { - qemu_mutex_lock(&comp_param[idx].mutex); - qemu_cond_signal(&comp_param[idx].cond); - qemu_mutex_unlock(&comp_param[idx].mutex); - } -} - -void migrate_compress_threads_join(void) -{ - int i, thread_count; - - if (!migrate_use_compression()) { - return; - } - terminate_compression_threads(); - thread_count = migrate_compress_threads(); - for (i = 0; i < thread_count; i++) { - qemu_thread_join(compress_threads + i); - qemu_fclose(comp_param[i].file); - qemu_mutex_destroy(&comp_param[i].mutex); - qemu_cond_destroy(&comp_param[i].cond); - } - qemu_mutex_destroy(comp_done_lock); - qemu_cond_destroy(comp_done_cond); - g_free(compress_threads); - g_free(comp_param); - g_free(comp_done_cond); - g_free(comp_done_lock); - compress_threads = NULL; - comp_param = NULL; - comp_done_cond = NULL; - comp_done_lock = NULL; -} - -void migrate_compress_threads_create(void) -{ - int i, thread_count; - - if (!migrate_use_compression()) { - return; - } - quit_comp_thread = false; - compression_switch = true; - thread_count = migrate_compress_threads(); - compress_threads = g_new0(QemuThread, thread_count); - comp_param = g_new0(CompressParam, thread_count); - comp_done_cond = g_new0(QemuCond, 1); - comp_done_lock = g_new0(QemuMutex, 1); - qemu_cond_init(comp_done_cond); - qemu_mutex_init(comp_done_lock); - for (i = 0; i < thread_count; i++) { - /* com_param[i].file is just used as a dummy buffer to save data, set - * it's ops to empty. - */ - comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops); - comp_param[i].done = true; - qemu_mutex_init(&comp_param[i].mutex); - qemu_cond_init(&comp_param[i].cond); - qemu_thread_create(compress_threads + i, "compress", - do_data_compress, comp_param + i, - QEMU_THREAD_JOINABLE); - } -} - -/** - * save_page_header: Write page header to wire - * - * If this is the 1st block, it also writes the block identification - * - * Returns: Number of bytes written - * - * @f: QEMUFile where to send the data - * @block: block that contains the page we want to send - * @offset: offset inside the block for the page - * in the lower bits, it contains flags - */ -static size_t save_page_header(QEMUFile *f, RAMBlock *block, ram_addr_t offset) -{ - size_t size; - - qemu_put_be64(f, offset); - size = 8; - - if (!(offset & RAM_SAVE_FLAG_CONTINUE)) { - qemu_put_byte(f, strlen(block->idstr)); - qemu_put_buffer(f, (uint8_t *)block->idstr, - strlen(block->idstr)); - size += 1 + strlen(block->idstr); - } - return size; -} - -/* Update the xbzrle cache to reflect a page that's been sent as all 0. - * The important thing is that a stale (not-yet-0'd) page be replaced - * by the new data. - * As a bonus, if the page wasn't in the cache it gets added so that - * when a small write is made into the 0'd page it gets XBZRLE sent - */ -static void xbzrle_cache_zero_page(ram_addr_t current_addr) -{ - if (ram_bulk_stage || !migrate_use_xbzrle()) { - return; - } - - /* We don't care if this fails to allocate a new cache page - * as long as it updated an old one */ - cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE, - bitmap_sync_count); -} - -#define ENCODING_FLAG_XBZRLE 0x1 - -/** - * save_xbzrle_page: compress and send current page - * - * Returns: 1 means that we wrote the page - * 0 means that page is identical to the one already sent - * -1 means that xbzrle would be longer than normal - * - * @f: QEMUFile where to send the data - * @current_data: - * @current_addr: - * @block: block that contains the page we want to send - * @offset: offset inside the block for the page - * @last_stage: if we are at the completion stage - * @bytes_transferred: increase it with the number of transferred bytes - */ -static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data, - ram_addr_t current_addr, RAMBlock *block, - ram_addr_t offset, bool last_stage, - uint64_t *bytes_transferred) -{ - int encoded_len = 0, bytes_xbzrle; - uint8_t *prev_cached_page; - - if (!cache_is_cached(XBZRLE.cache, current_addr, bitmap_sync_count)) { - acct_info.xbzrle_cache_miss++; - if (!last_stage) { - if (cache_insert(XBZRLE.cache, current_addr, *current_data, - bitmap_sync_count) == -1) { - return -1; - } else { - /* update *current_data when the page has been - inserted into cache */ - *current_data = get_cached_data(XBZRLE.cache, current_addr); - } - } - 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 */ - if (!last_stage) { - memcpy(prev_cached_page, *current_data, TARGET_PAGE_SIZE); - *current_data = prev_cached_page; - } - 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 */ - bytes_xbzrle = save_page_header(f, block, offset | 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_xbzrle += encoded_len + 1 + 2; - acct_info.xbzrle_pages++; - acct_info.xbzrle_bytes += bytes_xbzrle; - *bytes_transferred += bytes_xbzrle; - - return 1; -} - -static inline -ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr, - ram_addr_t start) -{ - unsigned long base = mr->ram_addr >> TARGET_PAGE_BITS; - unsigned long nr = base + (start >> TARGET_PAGE_BITS); - uint64_t mr_size = TARGET_PAGE_ALIGN(memory_region_size(mr)); - unsigned long size = base + (mr_size >> TARGET_PAGE_BITS); - - unsigned long next; - - if (ram_bulk_stage && nr > base) { - next = nr + 1; - } else { - next = find_next_bit(migration_bitmap, size, nr); - } - - if (next < size) { - clear_bit(next, migration_bitmap); - migration_dirty_pages--; - } - return (next - base) << TARGET_PAGE_BITS; -} - -static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length) -{ - migration_dirty_pages += - cpu_physical_memory_sync_dirty_bitmap(migration_bitmap, start, length); -} - - -/* Fix me: there are too many global variables used in migration process. */ -static int64_t start_time; -static int64_t bytes_xfer_prev; -static int64_t num_dirty_pages_period; -static uint64_t xbzrle_cache_miss_prev; -static uint64_t iterations_prev; - -static void migration_bitmap_sync_init(void) -{ - start_time = 0; - bytes_xfer_prev = 0; - num_dirty_pages_period = 0; - xbzrle_cache_miss_prev = 0; - iterations_prev = 0; -} - -/* Called with iothread lock held, to protect ram_list.dirty_memory[] */ -static void migration_bitmap_sync(void) -{ - RAMBlock *block; - uint64_t num_dirty_pages_init = migration_dirty_pages; - MigrationState *s = migrate_get_current(); - int64_t end_time; - int64_t bytes_xfer_now; - - bitmap_sync_count++; - - if (!bytes_xfer_prev) { - bytes_xfer_prev = ram_bytes_transferred(); - } - - if (!start_time) { - start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - } - - trace_migration_bitmap_sync_start(); - address_space_sync_dirty_bitmap(&address_space_memory); - - rcu_read_lock(); - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - migration_bitmap_sync_range(block->mr->ram_addr, block->used_length); - } - rcu_read_unlock(); - - trace_migration_bitmap_sync_end(migration_dirty_pages - - num_dirty_pages_init); - num_dirty_pages_period += migration_dirty_pages - num_dirty_pages_init; - end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - - /* more than 1 second = 1000 millisecons */ - if (end_time > start_time + 1000) { - if (migrate_auto_converge()) { - /* The following detection logic can be refined later. For now: - Check to see if the dirtied bytes is 50% more than the approx. - amount of bytes that just got transferred since the last time we - were in this routine. If that happens >N times (for now N==4) - we turn on the throttle down logic */ - bytes_xfer_now = ram_bytes_transferred(); - if (s->dirty_pages_rate && - (num_dirty_pages_period * TARGET_PAGE_SIZE > - (bytes_xfer_now - bytes_xfer_prev)/2) && - (dirty_rate_high_cnt++ > 4)) { - trace_migration_throttle(); - mig_throttle_on = true; - dirty_rate_high_cnt = 0; - } - bytes_xfer_prev = bytes_xfer_now; - } else { - mig_throttle_on = false; - } - if (migrate_use_xbzrle()) { - if (iterations_prev != acct_info.iterations) { - acct_info.xbzrle_cache_miss_rate = - (double)(acct_info.xbzrle_cache_miss - - xbzrle_cache_miss_prev) / - (acct_info.iterations - iterations_prev); - } - iterations_prev = acct_info.iterations; - xbzrle_cache_miss_prev = acct_info.xbzrle_cache_miss; - } - s->dirty_pages_rate = num_dirty_pages_period * 1000 - / (end_time - start_time); - s->dirty_bytes_rate = s->dirty_pages_rate * TARGET_PAGE_SIZE; - start_time = end_time; - num_dirty_pages_period = 0; - } - s->dirty_sync_count = bitmap_sync_count; -} - -/** - * save_zero_page: Send the zero page to the stream - * - * Returns: Number of pages written. - * - * @f: QEMUFile where to send the data - * @block: block that contains the page we want to send - * @offset: offset inside the block for the page - * @p: pointer to the page - * @bytes_transferred: increase it with the number of transferred bytes - */ -static int save_zero_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset, - uint8_t *p, uint64_t *bytes_transferred) -{ - int pages = -1; - - if (is_zero_range(p, TARGET_PAGE_SIZE)) { - acct_info.dup_pages++; - *bytes_transferred += save_page_header(f, block, - offset | RAM_SAVE_FLAG_COMPRESS); - qemu_put_byte(f, 0); - *bytes_transferred += 1; - pages = 1; - } - - return pages; -} - -/** - * ram_save_page: Send the given page to the stream - * - * Returns: Number of pages written. - * - * @f: QEMUFile where to send the data - * @block: block that contains the page we want to send - * @offset: offset inside the block for the page - * @last_stage: if we are at the completion stage - * @bytes_transferred: increase it with the number of transferred bytes - */ -static int ram_save_page(QEMUFile *f, RAMBlock* block, ram_addr_t offset, - bool last_stage, uint64_t *bytes_transferred) -{ - int pages = -1; - uint64_t bytes_xmit; - ram_addr_t current_addr; - MemoryRegion *mr = block->mr; - uint8_t *p; - int ret; - bool send_async = true; - - p = memory_region_get_ram_ptr(mr) + offset; - - /* In doubt sent page as normal */ - bytes_xmit = 0; - ret = ram_control_save_page(f, block->offset, - offset, TARGET_PAGE_SIZE, &bytes_xmit); - if (bytes_xmit) { - *bytes_transferred += bytes_xmit; - pages = 1; - } - - XBZRLE_cache_lock(); - - current_addr = block->offset + offset; - - if (block == last_sent_block) { - offset |= RAM_SAVE_FLAG_CONTINUE; - } - if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { - if (ret != RAM_SAVE_CONTROL_DELAYED) { - if (bytes_xmit > 0) { - acct_info.norm_pages++; - } else if (bytes_xmit == 0) { - acct_info.dup_pages++; - } - } - } else { - pages = save_zero_page(f, block, offset, p, bytes_transferred); - if (pages > 0) { - /* Must let xbzrle know, otherwise a previous (now 0'd) cached - * page would be stale - */ - xbzrle_cache_zero_page(current_addr); - } else if (!ram_bulk_stage && migrate_use_xbzrle()) { - pages = save_xbzrle_page(f, &p, current_addr, block, - offset, last_stage, bytes_transferred); - if (!last_stage) { - /* Can't send this cached data async, since the cache page - * might get updated before it gets to the wire - */ - send_async = false; - } - } - } - - /* XBZRLE overflow or normal page */ - if (pages == -1) { - *bytes_transferred += save_page_header(f, block, - offset | RAM_SAVE_FLAG_PAGE); - if (send_async) { - qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE); - } else { - qemu_put_buffer(f, p, TARGET_PAGE_SIZE); - } - *bytes_transferred += TARGET_PAGE_SIZE; - pages = 1; - acct_info.norm_pages++; - } - - XBZRLE_cache_unlock(); - - return pages; -} - -static int do_compress_ram_page(CompressParam *param) -{ - int bytes_sent, blen; - uint8_t *p; - RAMBlock *block = param->block; - ram_addr_t offset = param->offset; - - p = memory_region_get_ram_ptr(block->mr) + (offset & TARGET_PAGE_MASK); - - bytes_sent = save_page_header(param->file, block, offset | - RAM_SAVE_FLAG_COMPRESS_PAGE); - blen = qemu_put_compression_data(param->file, p, TARGET_PAGE_SIZE, - migrate_compress_level()); - bytes_sent += blen; - - return bytes_sent; -} - -static inline void start_compression(CompressParam *param) -{ - param->done = false; - qemu_mutex_lock(¶m->mutex); - param->start = true; - qemu_cond_signal(¶m->cond); - qemu_mutex_unlock(¶m->mutex); -} - -static inline void start_decompression(DecompressParam *param) -{ - qemu_mutex_lock(¶m->mutex); - param->start = true; - qemu_cond_signal(¶m->cond); - qemu_mutex_unlock(¶m->mutex); -} - -static uint64_t bytes_transferred; - -static void flush_compressed_data(QEMUFile *f) -{ - int idx, len, thread_count; - - if (!migrate_use_compression()) { - return; - } - thread_count = migrate_compress_threads(); - for (idx = 0; idx < thread_count; idx++) { - if (!comp_param[idx].done) { - qemu_mutex_lock(comp_done_lock); - while (!comp_param[idx].done && !quit_comp_thread) { - qemu_cond_wait(comp_done_cond, comp_done_lock); - } - qemu_mutex_unlock(comp_done_lock); - } - if (!quit_comp_thread) { - len = qemu_put_qemu_file(f, comp_param[idx].file); - bytes_transferred += len; - } - } -} - -static inline void set_compress_params(CompressParam *param, RAMBlock *block, - ram_addr_t offset) -{ - param->block = block; - param->offset = offset; -} - -static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block, - ram_addr_t offset, - uint64_t *bytes_transferred) -{ - int idx, thread_count, bytes_xmit = -1, pages = -1; - - thread_count = migrate_compress_threads(); - qemu_mutex_lock(comp_done_lock); - while (true) { - for (idx = 0; idx < thread_count; idx++) { - if (comp_param[idx].done) { - bytes_xmit = qemu_put_qemu_file(f, comp_param[idx].file); - set_compress_params(&comp_param[idx], block, offset); - start_compression(&comp_param[idx]); - pages = 1; - acct_info.norm_pages++; - *bytes_transferred += bytes_xmit; - break; - } - } - if (pages > 0) { - break; - } else { - qemu_cond_wait(comp_done_cond, comp_done_lock); - } - } - qemu_mutex_unlock(comp_done_lock); - - return pages; -} - -/** - * ram_save_compressed_page: compress the given page and send it to the stream - * - * Returns: Number of pages written. - * - * @f: QEMUFile where to send the data - * @block: block that contains the page we want to send - * @offset: offset inside the block for the page - * @last_stage: if we are at the completion stage - * @bytes_transferred: increase it with the number of transferred bytes - */ -static int ram_save_compressed_page(QEMUFile *f, RAMBlock *block, - ram_addr_t offset, bool last_stage, - uint64_t *bytes_transferred) -{ - int pages = -1; - uint64_t bytes_xmit; - MemoryRegion *mr = block->mr; - uint8_t *p; - int ret; - - p = memory_region_get_ram_ptr(mr) + offset; - - bytes_xmit = 0; - ret = ram_control_save_page(f, block->offset, - offset, TARGET_PAGE_SIZE, &bytes_xmit); - if (bytes_xmit) { - *bytes_transferred += bytes_xmit; - pages = 1; - } - if (block == last_sent_block) { - offset |= RAM_SAVE_FLAG_CONTINUE; - } - if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { - if (ret != RAM_SAVE_CONTROL_DELAYED) { - if (bytes_xmit > 0) { - acct_info.norm_pages++; - } else if (bytes_xmit == 0) { - acct_info.dup_pages++; - } - } - } else { - /* When starting the process of a new block, the first page of - * the block should be sent out before other pages in the same - * block, and all the pages in last block should have been sent - * out, keeping this order is important, because the 'cont' flag - * is used to avoid resending the block name. - */ - if (block != last_sent_block) { - flush_compressed_data(f); - pages = save_zero_page(f, block, offset, p, bytes_transferred); - if (pages == -1) { - set_compress_params(&comp_param[0], block, offset); - /* Use the qemu thread to compress the data to make sure the - * first page is sent out before other pages - */ - bytes_xmit = do_compress_ram_page(&comp_param[0]); - acct_info.norm_pages++; - qemu_put_qemu_file(f, comp_param[0].file); - *bytes_transferred += bytes_xmit; - pages = 1; - } - } else { - pages = save_zero_page(f, block, offset, p, bytes_transferred); - if (pages == -1) { - pages = compress_page_with_multi_thread(f, block, offset, - bytes_transferred); - } - } - } - - return pages; -} - -/** - * ram_find_and_save_block: Finds a dirty page and sends it to f - * - * Called within an RCU critical section. - * - * Returns: The number of pages written - * 0 means no dirty pages - * - * @f: QEMUFile where to send the data - * @last_stage: if we are at the completion stage - * @bytes_transferred: increase it with the number of transferred bytes - */ - -static int ram_find_and_save_block(QEMUFile *f, bool last_stage, - uint64_t *bytes_transferred) -{ - RAMBlock *block = last_seen_block; - ram_addr_t offset = last_offset; - bool complete_round = false; - int pages = 0; - MemoryRegion *mr; - - if (!block) - block = QLIST_FIRST_RCU(&ram_list.blocks); - - while (true) { - mr = block->mr; - offset = migration_bitmap_find_and_reset_dirty(mr, offset); - if (complete_round && block == last_seen_block && - offset >= last_offset) { - break; - } - if (offset >= block->used_length) { - offset = 0; - block = QLIST_NEXT_RCU(block, next); - if (!block) { - block = QLIST_FIRST_RCU(&ram_list.blocks); - complete_round = true; - ram_bulk_stage = false; - if (migrate_use_xbzrle()) { - /* If xbzrle is on, stop using the data compression at this - * point. In theory, xbzrle can do better than compression. - */ - flush_compressed_data(f); - compression_switch = false; - } - } - } else { - if (compression_switch && migrate_use_compression()) { - pages = ram_save_compressed_page(f, block, offset, last_stage, - bytes_transferred); - } else { - pages = ram_save_page(f, block, offset, last_stage, - bytes_transferred); - } - - /* if page is unmodified, continue to the next */ - if (pages > 0) { - last_sent_block = block; - break; - } - } - } - - last_seen_block = block; - last_offset = offset; - - return pages; -} - -void acct_update_position(QEMUFile *f, size_t size, bool zero) -{ - uint64_t pages = size / TARGET_PAGE_SIZE; - if (zero) { - acct_info.dup_pages += pages; - } else { - acct_info.norm_pages += pages; - bytes_transferred += size; - qemu_update_position(f, size); - } -} - -static ram_addr_t ram_save_remaining(void) -{ - return migration_dirty_pages; -} - -uint64_t ram_bytes_remaining(void) -{ - return ram_save_remaining() * TARGET_PAGE_SIZE; -} - -uint64_t ram_bytes_transferred(void) -{ - return bytes_transferred; -} - -uint64_t ram_bytes_total(void) -{ - RAMBlock *block; - uint64_t total = 0; - - rcu_read_lock(); - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) - total += block->used_length; - rcu_read_unlock(); - return total; -} - -void free_xbzrle_decoded_buf(void) -{ - g_free(xbzrle_decoded_buf); - xbzrle_decoded_buf = NULL; -} - -static void migration_end(void) -{ - if (migration_bitmap) { - memory_global_dirty_log_stop(); - g_free(migration_bitmap); - migration_bitmap = NULL; - } - - XBZRLE_cache_lock(); - if (XBZRLE.cache) { - cache_fini(XBZRLE.cache); - g_free(XBZRLE.encoded_buf); - g_free(XBZRLE.current_buf); - XBZRLE.cache = NULL; - XBZRLE.encoded_buf = NULL; - XBZRLE.current_buf = NULL; - } - XBZRLE_cache_unlock(); -} - -static void ram_migration_cancel(void *opaque) -{ - migration_end(); -} - -static void reset_ram_globals(void) -{ - last_seen_block = NULL; - last_sent_block = NULL; - last_offset = 0; - last_version = ram_list.version; - ram_bulk_stage = true; -} - -#define MAX_WAIT 50 /* ms, half buffered_file limit */ - - -/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has - * long-running RCU critical section. When rcu-reclaims in the code - * start to become numerous it will be necessary to reduce the - * granularity of these critical sections. - */ - -static int ram_save_setup(QEMUFile *f, void *opaque) -{ - RAMBlock *block; - int64_t ram_bitmap_pages; /* Size of bitmap in pages, including gaps */ - - mig_throttle_on = false; - dirty_rate_high_cnt = 0; - bitmap_sync_count = 0; - migration_bitmap_sync_init(); - - if (migrate_use_xbzrle()) { - XBZRLE_cache_lock(); - XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() / - TARGET_PAGE_SIZE, - TARGET_PAGE_SIZE); - if (!XBZRLE.cache) { - XBZRLE_cache_unlock(); - error_report("Error creating cache"); - return -1; - } - XBZRLE_cache_unlock(); - - /* We prefer not to abort if there is no memory */ - XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE); - if (!XBZRLE.encoded_buf) { - error_report("Error allocating encoded_buf"); - return -1; - } - - XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE); - if (!XBZRLE.current_buf) { - error_report("Error allocating current_buf"); - g_free(XBZRLE.encoded_buf); - XBZRLE.encoded_buf = NULL; - return -1; - } - - acct_clear(); - } - - /* iothread lock needed for ram_list.dirty_memory[] */ - qemu_mutex_lock_iothread(); - qemu_mutex_lock_ramlist(); - rcu_read_lock(); - bytes_transferred = 0; - reset_ram_globals(); - - ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS; - migration_bitmap = bitmap_new(ram_bitmap_pages); - bitmap_set(migration_bitmap, 0, ram_bitmap_pages); - - /* - * Count the total number of pages used by ram blocks not including any - * gaps due to alignment or unplugs. - */ - migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS; - - memory_global_dirty_log_start(); - migration_bitmap_sync(); - qemu_mutex_unlock_ramlist(); - qemu_mutex_unlock_iothread(); - - qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); - - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - qemu_put_byte(f, strlen(block->idstr)); - qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); - qemu_put_be64(f, block->used_length); - } - - rcu_read_unlock(); - - ram_control_before_iterate(f, RAM_CONTROL_SETUP); - ram_control_after_iterate(f, RAM_CONTROL_SETUP); - - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - - return 0; -} - -static int ram_save_iterate(QEMUFile *f, void *opaque) -{ - int ret; - int i; - int64_t t0; - int pages_sent = 0; - - rcu_read_lock(); - if (ram_list.version != last_version) { - reset_ram_globals(); - } - - /* Read version before ram_list.blocks */ - smp_rmb(); - - ram_control_before_iterate(f, RAM_CONTROL_ROUND); - - t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - i = 0; - while ((ret = qemu_file_rate_limit(f)) == 0) { - int pages; - - pages = ram_find_and_save_block(f, false, &bytes_transferred); - /* no more pages to sent */ - if (pages == 0) { - break; - } - pages_sent += pages; - acct_info.iterations++; - check_guest_throttling(); - /* 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 - iterations - */ - if ((i & 63) == 0) { - uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / 1000000; - if (t1 > MAX_WAIT) { - DPRINTF("big wait: %" PRIu64 " milliseconds, %d iterations\n", - t1, i); - break; - } - } - i++; - } - flush_compressed_data(f); - rcu_read_unlock(); - - /* - * Must occur before EOS (or any QEMUFile operation) - * because of RDMA protocol. - */ - ram_control_after_iterate(f, RAM_CONTROL_ROUND); - - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - bytes_transferred += 8; - - ret = qemu_file_get_error(f); - if (ret < 0) { - return ret; - } - - return pages_sent; -} - -/* Called with iothread lock */ -static int ram_save_complete(QEMUFile *f, void *opaque) -{ - rcu_read_lock(); - - migration_bitmap_sync(); - - ram_control_before_iterate(f, RAM_CONTROL_FINISH); - - /* try transferring iterative blocks of memory */ - - /* flush all remaining blocks regardless of rate limiting */ - while (true) { - int pages; - - pages = ram_find_and_save_block(f, true, &bytes_transferred); - /* no more blocks to sent */ - if (pages == 0) { - break; - } - } - - flush_compressed_data(f); - ram_control_after_iterate(f, RAM_CONTROL_FINISH); - migration_end(); - - rcu_read_unlock(); - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - - return 0; -} - -static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) -{ - uint64_t remaining_size; - - remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; - - if (remaining_size < max_size) { - qemu_mutex_lock_iothread(); - rcu_read_lock(); - migration_bitmap_sync(); - rcu_read_unlock(); - qemu_mutex_unlock_iothread(); - remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; - } - return remaining_size; -} - -static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) -{ - 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) { - error_report("Failed to load XBZRLE page - wrong compression!"); - return -1; - } - - if (xh_len > TARGET_PAGE_SIZE) { - error_report("Failed to load XBZRLE page - len overflow!"); - return -1; - } - /* load data and decode */ - qemu_get_buffer(f, xbzrle_decoded_buf, xh_len); - - /* decode RLE */ - if (xbzrle_decode_buffer(xbzrle_decoded_buf, xh_len, host, - TARGET_PAGE_SIZE) == -1) { - error_report("Failed to load XBZRLE page - decode error!"); - return -1; - } - - return 0; -} - -/* Must be called from within a rcu critical section. - * Returns a pointer from within the RCU-protected ram_list. - */ -static inline void *host_from_stream_offset(QEMUFile *f, - ram_addr_t offset, - int flags) -{ - static RAMBlock *block = NULL; - char id[256]; - uint8_t len; - - if (flags & RAM_SAVE_FLAG_CONTINUE) { - if (!block || block->max_length <= offset) { - error_report("Ack, bad migration stream!"); - return NULL; - } - - return memory_region_get_ram_ptr(block->mr) + offset; - } - - len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)id, len); - id[len] = 0; - - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - if (!strncmp(id, block->idstr, sizeof(id)) && - block->max_length > offset) { - return memory_region_get_ram_ptr(block->mr) + offset; - } - } - - error_report("Can't find block %s!", id); - return NULL; -} - -/* - * If a page (or a whole RDMA chunk) has been - * determined to be zero, then zap it. - */ -void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) -{ - if (ch != 0 || !is_zero_range(host, size)) { - memset(host, ch, size); - } -} - -static void *do_data_decompress(void *opaque) -{ - DecompressParam *param = opaque; - unsigned long pagesize; - - while (!quit_decomp_thread) { - qemu_mutex_lock(¶m->mutex); - while (!param->start && !quit_decomp_thread) { - qemu_cond_wait(¶m->cond, ¶m->mutex); - pagesize = TARGET_PAGE_SIZE; - if (!quit_decomp_thread) { - /* uncompress() will return failed in some case, especially - * when the page is dirted when doing the compression, it's - * not a problem because the dirty page will be retransferred - * and uncompress() won't break the data in other pages. - */ - uncompress((Bytef *)param->des, &pagesize, - (const Bytef *)param->compbuf, param->len); - } - param->start = false; - } - qemu_mutex_unlock(¶m->mutex); - } - - return NULL; -} - -void migrate_decompress_threads_create(void) -{ - int i, thread_count; - - thread_count = migrate_decompress_threads(); - decompress_threads = g_new0(QemuThread, thread_count); - decomp_param = g_new0(DecompressParam, thread_count); - compressed_data_buf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); - quit_decomp_thread = false; - for (i = 0; i < thread_count; i++) { - qemu_mutex_init(&decomp_param[i].mutex); - qemu_cond_init(&decomp_param[i].cond); - decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); - qemu_thread_create(decompress_threads + i, "decompress", - do_data_decompress, decomp_param + i, - QEMU_THREAD_JOINABLE); - } -} - -void migrate_decompress_threads_join(void) -{ - int i, thread_count; - - quit_decomp_thread = true; - thread_count = migrate_decompress_threads(); - for (i = 0; i < thread_count; i++) { - qemu_mutex_lock(&decomp_param[i].mutex); - qemu_cond_signal(&decomp_param[i].cond); - qemu_mutex_unlock(&decomp_param[i].mutex); - } - for (i = 0; i < thread_count; i++) { - qemu_thread_join(decompress_threads + i); - qemu_mutex_destroy(&decomp_param[i].mutex); - qemu_cond_destroy(&decomp_param[i].cond); - g_free(decomp_param[i].compbuf); - } - g_free(decompress_threads); - g_free(decomp_param); - g_free(compressed_data_buf); - decompress_threads = NULL; - decomp_param = NULL; - compressed_data_buf = NULL; -} - -static void decompress_data_with_multi_threads(uint8_t *compbuf, - void *host, int len) -{ - int idx, thread_count; - - thread_count = migrate_decompress_threads(); - while (true) { - for (idx = 0; idx < thread_count; idx++) { - if (!decomp_param[idx].start) { - memcpy(decomp_param[idx].compbuf, compbuf, len); - decomp_param[idx].des = host; - decomp_param[idx].len = len; - start_decompression(&decomp_param[idx]); - break; - } - } - if (idx < thread_count) { - break; - } - } -} - -static int ram_load(QEMUFile *f, void *opaque, int version_id) -{ - int flags = 0, ret = 0; - static uint64_t seq_iter; - int len = 0; - - seq_iter++; - - if (version_id != 4) { - ret = -EINVAL; - } - - /* This RCU critical section can be very long running. - * When RCU reclaims in the code start to become numerous, - * it will be necessary to reduce the granularity of this - * critical section. - */ - rcu_read_lock(); - while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { - ram_addr_t addr, total_ram_bytes; - void *host; - uint8_t ch; - - addr = qemu_get_be64(f); - flags = addr & ~TARGET_PAGE_MASK; - addr &= TARGET_PAGE_MASK; - - switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { - case RAM_SAVE_FLAG_MEM_SIZE: - /* Synchronize RAM block list */ - total_ram_bytes = addr; - while (!ret && total_ram_bytes) { - RAMBlock *block; - uint8_t len; - char id[256]; - ram_addr_t length; - - len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)id, len); - id[len] = 0; - length = qemu_get_be64(f); - - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - if (!strncmp(id, block->idstr, sizeof(id))) { - if (length != block->used_length) { - Error *local_err = NULL; - - ret = qemu_ram_resize(block->offset, length, &local_err); - if (local_err) { - error_report_err(local_err); - } - } - break; - } - } - - if (!block) { - error_report("Unknown ramblock \"%s\", cannot " - "accept migration", id); - ret = -EINVAL; - } - - total_ram_bytes -= length; - } - break; - case RAM_SAVE_FLAG_COMPRESS: - host = host_from_stream_offset(f, addr, flags); - if (!host) { - error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); - ret = -EINVAL; - break; - } - ch = qemu_get_byte(f); - ram_handle_compressed(host, ch, TARGET_PAGE_SIZE); - break; - case RAM_SAVE_FLAG_PAGE: - host = host_from_stream_offset(f, addr, flags); - if (!host) { - error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); - ret = -EINVAL; - break; - } - qemu_get_buffer(f, host, TARGET_PAGE_SIZE); - break; - case RAM_SAVE_FLAG_COMPRESS_PAGE: - host = host_from_stream_offset(f, addr, flags); - if (!host) { - error_report("Invalid RAM offset " RAM_ADDR_FMT, addr); - ret = -EINVAL; - break; - } - - len = qemu_get_be32(f); - if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { - error_report("Invalid compressed data length: %d", len); - ret = -EINVAL; - break; - } - qemu_get_buffer(f, compressed_data_buf, len); - decompress_data_with_multi_threads(compressed_data_buf, host, len); - break; - case RAM_SAVE_FLAG_XBZRLE: - host = host_from_stream_offset(f, addr, flags); - if (!host) { - error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); - ret = -EINVAL; - break; - } - if (load_xbzrle(f, addr, host) < 0) { - error_report("Failed to decompress XBZRLE page at " - RAM_ADDR_FMT, addr); - ret = -EINVAL; - break; - } - break; - case RAM_SAVE_FLAG_EOS: - /* normal exit */ - break; - default: - if (flags & RAM_SAVE_FLAG_HOOK) { - ram_control_load_hook(f, flags); - } else { - error_report("Unknown combination of migration flags: %#x", - flags); - ret = -EINVAL; - } - } - if (!ret) { - ret = qemu_file_get_error(f); - } - } - - rcu_read_unlock(); - DPRINTF("Completed load of VM with exit code %d seq iteration " - "%" PRIu64 "\n", ret, seq_iter); - return ret; -} - -static SaveVMHandlers savevm_ram_handlers = { - .save_live_setup = ram_save_setup, - .save_live_iterate = ram_save_iterate, - .save_live_complete = ram_save_complete, - .save_live_pending = ram_save_pending, - .load_state = ram_load, - .cancel = ram_migration_cancel, -}; - -void ram_mig_init(void) -{ - qemu_mutex_init(&XBZRLE.lock); - register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL); -} - struct soundhw { const char *name; const char *descr; @@ -1869,52 +307,3 @@ TargetInfo *qmp_query_target(Error **errp) return info; } - -/* Stub function that's gets run on the vcpu when its brought out of the - VM to run inside qemu via async_run_on_cpu()*/ -static void mig_sleep_cpu(void *opq) -{ - qemu_mutex_unlock_iothread(); - g_usleep(30*1000); - qemu_mutex_lock_iothread(); -} - -/* To reduce the dirty rate explicitly disallow the VCPUs from spending - much time in the VM. The migration thread will try to catchup. - Workload will experience a performance drop. -*/ -static void mig_throttle_guest_down(void) -{ - CPUState *cpu; - - qemu_mutex_lock_iothread(); - CPU_FOREACH(cpu) { - async_run_on_cpu(cpu, mig_sleep_cpu, NULL); - } - qemu_mutex_unlock_iothread(); -} - -static void check_guest_throttling(void) -{ - static int64_t t0; - int64_t t1; - - if (!mig_throttle_on) { - return; - } - - if (!t0) { - t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - return; - } - - t1 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - - /* If it has been more than 40 ms since the last time the guest - * was throttled then do it again. - */ - if (40 < (t1-t0)/1000000) { - mig_throttle_guest_down(); - t0 = t1; - } -} @@ -280,6 +280,12 @@ static void aio_timerlist_notify(void *opaque) aio_notify(opaque); } +static void aio_rfifolock_cb(void *opaque) +{ + /* Kick owner thread in case they are blocked in aio_poll() */ + aio_notify(opaque); +} + AioContext *aio_context_new(Error **errp) { int ret; @@ -297,7 +303,7 @@ AioContext *aio_context_new(Error **errp) event_notifier_test_and_clear); ctx->thread_pool = NULL; qemu_mutex_init(&ctx->bh_lock); - rfifolock_init(&ctx->lock, NULL, NULL); + rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx); timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); return ctx; diff --git a/audio/Makefile.objs b/audio/Makefile.objs index 26a0ac9..481d1aa 100644 --- a/audio/Makefile.objs +++ b/audio/Makefile.objs @@ -5,13 +5,9 @@ common-obj-$(CONFIG_SPICE) += spiceaudio.o common-obj-$(CONFIG_COREAUDIO) += coreaudio.o common-obj-$(CONFIG_ALSA) += alsaaudio.o common-obj-$(CONFIG_DSOUND) += dsoundaudio.o -common-obj-$(CONFIG_FMOD) += fmodaudio.o -common-obj-$(CONFIG_ESD) += esdaudio.o common-obj-$(CONFIG_PA) += paaudio.o -common-obj-$(CONFIG_WINWAVE) += winwaveaudio.o common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o common-obj-y += wavcapture.o -$(obj)/audio.o $(obj)/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS) sdlaudio.o-cflags := $(SDL_CFLAGS) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 74ead97..6315b2d 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -25,6 +25,7 @@ #include "qemu-common.h" #include "qemu/main-loop.h" #include "audio.h" +#include "trace.h" #if QEMU_GNUC_PREREQ(4, 3) #pragma GCC diagnostic ignored "-Waddress" @@ -33,9 +34,28 @@ #define AUDIO_CAP "alsa" #include "audio_int.h" +typedef struct ALSAConf { + int size_in_usec_in; + int size_in_usec_out; + const char *pcm_name_in; + const char *pcm_name_out; + unsigned int buffer_size_in; + unsigned int period_size_in; + unsigned int buffer_size_out; + unsigned int period_size_out; + unsigned int threshold; + + int buffer_size_in_overridden; + int period_size_in_overridden; + + int buffer_size_out_overridden; + int period_size_out_overridden; +} ALSAConf; + struct pollhlp { snd_pcm_t *handle; struct pollfd *pfds; + ALSAConf *conf; int count; int mask; }; @@ -56,30 +76,6 @@ typedef struct ALSAVoiceIn { struct pollhlp pollhlp; } ALSAVoiceIn; -static struct { - int size_in_usec_in; - int size_in_usec_out; - const char *pcm_name_in; - const char *pcm_name_out; - unsigned int buffer_size_in; - unsigned int period_size_in; - unsigned int buffer_size_out; - unsigned int period_size_out; - unsigned int threshold; - - int buffer_size_in_overridden; - int period_size_in_overridden; - - int buffer_size_out_overridden; - int period_size_out_overridden; - int verbose; -} conf = { - .buffer_size_out = 4096, - .period_size_out = 1024, - .pcm_name_out = "default", - .pcm_name_in = "default", -}; - struct alsa_params_req { int freq; snd_pcm_format_t fmt; @@ -205,9 +201,7 @@ static void alsa_poll_handler (void *opaque) } if (!(revents & hlp->mask)) { - if (conf.verbose) { - dolog ("revents = %d\n", revents); - } + trace_alsa_revents(revents); return; } @@ -266,31 +260,14 @@ static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask) for (i = 0; i < count; ++i) { if (pfds[i].events & POLLIN) { - err = qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, - NULL, hlp); + qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, NULL, hlp); } if (pfds[i].events & POLLOUT) { - if (conf.verbose) { - dolog ("POLLOUT %d %d\n", i, pfds[i].fd); - } - err = qemu_set_fd_handler (pfds[i].fd, NULL, - alsa_poll_handler, hlp); + trace_alsa_pollout(i, pfds[i].fd); + qemu_set_fd_handler (pfds[i].fd, NULL, alsa_poll_handler, hlp); } - if (conf.verbose) { - dolog ("Set handler events=%#x index=%d fd=%d err=%d\n", - pfds[i].events, i, pfds[i].fd, err); - } - - if (err) { - dolog ("Failed to set handler events=%#x index=%d fd=%d err=%d\n", - pfds[i].events, i, pfds[i].fd, err); + trace_alsa_set_handler(pfds[i].events, i, pfds[i].fd, err); - while (i--) { - qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL); - } - g_free (pfds); - return -1; - } } hlp->pfds = pfds; hlp->count = count; @@ -476,14 +453,15 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) } static int alsa_open (int in, struct alsa_params_req *req, - struct alsa_params_obt *obt, snd_pcm_t **handlep) + struct alsa_params_obt *obt, snd_pcm_t **handlep, + ALSAConf *conf) { snd_pcm_t *handle; snd_pcm_hw_params_t *hw_params; int err; int size_in_usec; unsigned int freq, nchannels; - const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out; + const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out; snd_pcm_uframes_t obt_buffer_size; const char *typ = in ? "ADC" : "DAC"; snd_pcm_format_t obtfmt; @@ -522,7 +500,7 @@ static int alsa_open (int in, struct alsa_params_req *req, } err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt); - if (err < 0 && conf.verbose) { + if (err < 0) { alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt); } @@ -654,7 +632,7 @@ static int alsa_open (int in, struct alsa_params_req *req, goto err; } - if (!in && conf.threshold) { + if (!in && conf->threshold) { snd_pcm_uframes_t threshold; int bytes_per_sec; @@ -676,7 +654,7 @@ static int alsa_open (int in, struct alsa_params_req *req, break; } - threshold = (conf.threshold * bytes_per_sec) / 1000; + threshold = (conf->threshold * bytes_per_sec) / 1000; alsa_set_threshold (handle, threshold); } @@ -686,10 +664,9 @@ static int alsa_open (int in, struct alsa_params_req *req, *handlep = handle; - if (conf.verbose && - (obtfmt != req->fmt || + if (obtfmt != req->fmt || obt->nchannels != req->nchannels || - obt->freq != req->freq)) { + obt->freq != req->freq) { dolog ("Audio parameters for %s\n", typ); alsa_dump_info (req, obt, obtfmt); } @@ -743,9 +720,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa) if (written <= 0) { switch (written) { case 0: - if (conf.verbose) { - dolog ("Failed to write %d frames (wrote zero)\n", len); - } + trace_alsa_wrote_zero(len); return; case -EPIPE: @@ -754,9 +729,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa) len); return; } - if (conf.verbose) { - dolog ("Recovering from playback xrun\n"); - } + trace_alsa_xrun_out(); continue; case -ESTRPIPE: @@ -767,9 +740,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa) len); return; } - if (conf.verbose) { - dolog ("Resuming suspended output stream\n"); - } + trace_alsa_resume_out(); continue; case -EAGAIN: @@ -819,25 +790,27 @@ static void alsa_fini_out (HWVoiceOut *hw) alsa->pcm_buf = NULL; } -static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) +static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) { ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; struct alsa_params_req req; struct alsa_params_obt obt; snd_pcm_t *handle; struct audsettings obt_as; + ALSAConf *conf = drv_opaque; req.fmt = aud_to_alsafmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; - req.period_size = conf.period_size_out; - req.buffer_size = conf.buffer_size_out; - req.size_in_usec = conf.size_in_usec_out; + req.period_size = conf->period_size_out; + req.buffer_size = conf->buffer_size_out; + req.size_in_usec = conf->size_in_usec_out; req.override_mask = - (conf.period_size_out_overridden ? 1 : 0) | - (conf.buffer_size_out_overridden ? 2 : 0); + (conf->period_size_out_overridden ? 1 : 0) | + (conf->buffer_size_out_overridden ? 2 : 0); - if (alsa_open (0, &req, &obt, &handle)) { + if (alsa_open (0, &req, &obt, &handle, conf)) { return -1; } @@ -858,6 +831,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) } alsa->handle = handle; + alsa->pollhlp.conf = conf; return 0; } @@ -928,25 +902,26 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) return -1; } -static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as) +static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) { ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; struct alsa_params_req req; struct alsa_params_obt obt; snd_pcm_t *handle; struct audsettings obt_as; + ALSAConf *conf = drv_opaque; req.fmt = aud_to_alsafmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; - req.period_size = conf.period_size_in; - req.buffer_size = conf.buffer_size_in; - req.size_in_usec = conf.size_in_usec_in; + req.period_size = conf->period_size_in; + req.buffer_size = conf->buffer_size_in; + req.size_in_usec = conf->size_in_usec_in; req.override_mask = - (conf.period_size_in_overridden ? 1 : 0) | - (conf.buffer_size_in_overridden ? 2 : 0); + (conf->period_size_in_overridden ? 1 : 0) | + (conf->buffer_size_in_overridden ? 2 : 0); - if (alsa_open (1, &req, &obt, &handle)) { + if (alsa_open (1, &req, &obt, &handle, conf)) { return -1; } @@ -967,6 +942,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as) } alsa->handle = handle; + alsa->pollhlp.conf = conf; return 0; } @@ -1022,14 +998,10 @@ static int alsa_run_in (HWVoiceIn *hw) dolog ("Failed to resume suspended input stream\n"); return 0; } - if (conf.verbose) { - dolog ("Resuming suspended input stream\n"); - } + trace_alsa_resume_in(); break; default: - if (conf.verbose) { - dolog ("No frames available and ALSA state is %d\n", state); - } + trace_alsa_no_frames(state); return 0; } } @@ -1064,9 +1036,7 @@ static int alsa_run_in (HWVoiceIn *hw) if (nread <= 0) { switch (nread) { case 0: - if (conf.verbose) { - dolog ("Failed to read %ld frames (read zero)\n", len); - } + trace_alsa_read_zero(len); goto exit; case -EPIPE: @@ -1074,9 +1044,7 @@ static int alsa_run_in (HWVoiceIn *hw) alsa_logerr (nread, "Failed to read %ld frames\n", len); goto exit; } - if (conf.verbose) { - dolog ("Recovering from capture xrun\n"); - } + trace_alsa_xrun_in(); continue; case -EAGAIN: @@ -1148,82 +1116,85 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) return -1; } +static ALSAConf glob_conf = { + .buffer_size_out = 4096, + .period_size_out = 1024, + .pcm_name_out = "default", + .pcm_name_in = "default", +}; + static void *alsa_audio_init (void) { - return &conf; + ALSAConf *conf = g_malloc(sizeof(ALSAConf)); + *conf = glob_conf; + return conf; } static void alsa_audio_fini (void *opaque) { - (void) opaque; + g_free(opaque); } static struct audio_option alsa_options[] = { { .name = "DAC_SIZE_IN_USEC", .tag = AUD_OPT_BOOL, - .valp = &conf.size_in_usec_out, + .valp = &glob_conf.size_in_usec_out, .descr = "DAC period/buffer size in microseconds (otherwise in frames)" }, { .name = "DAC_PERIOD_SIZE", .tag = AUD_OPT_INT, - .valp = &conf.period_size_out, + .valp = &glob_conf.period_size_out, .descr = "DAC period size (0 to go with system default)", - .overriddenp = &conf.period_size_out_overridden + .overriddenp = &glob_conf.period_size_out_overridden }, { .name = "DAC_BUFFER_SIZE", .tag = AUD_OPT_INT, - .valp = &conf.buffer_size_out, + .valp = &glob_conf.buffer_size_out, .descr = "DAC buffer size (0 to go with system default)", - .overriddenp = &conf.buffer_size_out_overridden + .overriddenp = &glob_conf.buffer_size_out_overridden }, { .name = "ADC_SIZE_IN_USEC", .tag = AUD_OPT_BOOL, - .valp = &conf.size_in_usec_in, + .valp = &glob_conf.size_in_usec_in, .descr = "ADC period/buffer size in microseconds (otherwise in frames)" }, { .name = "ADC_PERIOD_SIZE", .tag = AUD_OPT_INT, - .valp = &conf.period_size_in, + .valp = &glob_conf.period_size_in, .descr = "ADC period size (0 to go with system default)", - .overriddenp = &conf.period_size_in_overridden + .overriddenp = &glob_conf.period_size_in_overridden }, { .name = "ADC_BUFFER_SIZE", .tag = AUD_OPT_INT, - .valp = &conf.buffer_size_in, + .valp = &glob_conf.buffer_size_in, .descr = "ADC buffer size (0 to go with system default)", - .overriddenp = &conf.buffer_size_in_overridden + .overriddenp = &glob_conf.buffer_size_in_overridden }, { .name = "THRESHOLD", .tag = AUD_OPT_INT, - .valp = &conf.threshold, + .valp = &glob_conf.threshold, .descr = "(undocumented)" }, { .name = "DAC_DEV", .tag = AUD_OPT_STR, - .valp = &conf.pcm_name_out, + .valp = &glob_conf.pcm_name_out, .descr = "DAC device name (for instance dmix)" }, { .name = "ADC_DEV", .tag = AUD_OPT_STR, - .valp = &conf.pcm_name_in, + .valp = &glob_conf.pcm_name_in, .descr = "ADC device name" }, - { - .name = "VERBOSE", - .tag = AUD_OPT_BOOL, - .valp = &conf.verbose, - .descr = "Behave in a more verbose way" - }, { /* End of list */ } }; diff --git a/audio/audio.c b/audio/audio.c index 9d018e9..5be4b15 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -30,7 +30,6 @@ #define AUDIO_CAP "audio" #include "audio_int.h" -/* #define DEBUG_PLIVE */ /* #define DEBUG_LIVE */ /* #define DEBUG_OUT */ /* #define DEBUG_CAPTURE */ @@ -66,8 +65,6 @@ static struct { int hertz; int64_t ticks; } period; - int plive; - int log_to_monitor; int try_poll_in; int try_poll_out; } conf = { @@ -96,8 +93,6 @@ static struct { }, .period = { .hertz = 100 }, - .plive = 0, - .log_to_monitor = 0, .try_poll_in = 1, .try_poll_out = 1, }; @@ -331,20 +326,11 @@ static const char *audio_get_conf_str (const char *key, void AUD_vlog (const char *cap, const char *fmt, va_list ap) { - if (conf.log_to_monitor) { - if (cap) { - monitor_printf(default_mon, "%s: ", cap); - } - - monitor_vprintf(default_mon, fmt, ap); + if (cap) { + fprintf(stderr, "%s: ", cap); } - else { - if (cap) { - fprintf (stderr, "%s: ", cap); - } - vfprintf (stderr, fmt, ap); - } + vfprintf(stderr, fmt, ap); } void AUD_log (const char *cap, const char *fmt, ...) @@ -1454,9 +1440,6 @@ static void audio_run_out (AudioState *s) while (sw) { sw1 = sw->entries.le_next; if (!sw->active && !sw->callback.fn) { -#ifdef DEBUG_PLIVE - dolog ("Finishing with old voice\n"); -#endif audio_close_out (sw); } sw = sw1; @@ -1648,18 +1631,6 @@ static struct audio_option audio_options[] = { .valp = &conf.period.hertz, .descr = "Timer period in HZ (0 - use lowest possible)" }, - { - .name = "PLIVE", - .tag = AUD_OPT_BOOL, - .valp = &conf.plive, - .descr = "(undocumented)" - }, - { - .name = "LOG_TO_MONITOR", - .tag = AUD_OPT_BOOL, - .valp = &conf.log_to_monitor, - .descr = "Print logging messages to monitor instead of stderr" - }, { /* End of list */ } }; diff --git a/audio/audio_int.h b/audio/audio_int.h index fd019a0..566df5e 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -156,13 +156,13 @@ struct audio_driver { }; struct audio_pcm_ops { - int (*init_out)(HWVoiceOut *hw, struct audsettings *as); + int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque); void (*fini_out)(HWVoiceOut *hw); int (*run_out) (HWVoiceOut *hw, int live); int (*write) (SWVoiceOut *sw, void *buf, int size); int (*ctl_out) (HWVoiceOut *hw, int cmd, ...); - int (*init_in) (HWVoiceIn *hw, struct audsettings *as); + int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque); void (*fini_in) (HWVoiceIn *hw); int (*run_in) (HWVoiceIn *hw); int (*read) (SWVoiceIn *sw, void *buf, int size); @@ -206,14 +206,11 @@ extern struct audio_driver no_audio_driver; extern struct audio_driver oss_audio_driver; extern struct audio_driver sdl_audio_driver; extern struct audio_driver wav_audio_driver; -extern struct audio_driver fmod_audio_driver; extern struct audio_driver alsa_audio_driver; extern struct audio_driver coreaudio_audio_driver; extern struct audio_driver dsound_audio_driver; -extern struct audio_driver esd_audio_driver; extern struct audio_driver pa_audio_driver; extern struct audio_driver spice_audio_driver; -extern struct audio_driver winwave_audio_driver; extern const struct mixeng_volume nominal_volume; void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); diff --git a/audio/audio_template.h b/audio/audio_template.h index 584e536..99b27b2 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -262,7 +262,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) #ifdef DAC QLIST_INIT (&hw->cap_head); #endif - if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { + if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) { goto err0; } @@ -398,10 +398,6 @@ SW *glue (AUD_open_, TYPE) ( ) { AudioState *s = &glob_audio_state; -#ifdef DAC - int live = 0; - SW *old_sw = NULL; -#endif if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) { dolog ("card=%p name=%p callback_fn=%p as=%p\n", @@ -426,29 +422,6 @@ SW *glue (AUD_open_, TYPE) ( return sw; } -#ifdef DAC - if (conf.plive && sw && (!sw->active && !sw->empty)) { - live = sw->total_hw_samples_mixed; - -#ifdef DEBUG_PLIVE - dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live); - dolog ("Old %s freq %d, bits %d, channels %d\n", - SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels); - dolog ("New %s freq %d, bits %d, channels %d\n", - name, - as->freq, - (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8, - as->nchannels); -#endif - - if (live) { - old_sw = sw; - old_sw->callback.fn = NULL; - sw = NULL; - } - } -#endif - if (!glue (conf.fixed_, TYPE).enabled && sw) { glue (AUD_close_, TYPE) (card, sw); sw = NULL; @@ -481,20 +454,6 @@ SW *glue (AUD_open_, TYPE) ( sw->callback.fn = callback_fn; sw->callback.opaque = callback_opaque; -#ifdef DAC - if (live) { - int mixed = - (live << old_sw->info.shift) - * old_sw->info.bytes_per_second - / sw->info.bytes_per_second; - -#ifdef DEBUG_PLIVE - dolog ("Silence will be mixed %d\n", mixed); -#endif - sw->total_hw_samples_mixed += mixed; - } -#endif - #ifdef DEBUG_AUDIO dolog ("%s\n", name); audio_pcm_print_info ("hw", &sw->hw->info); diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 5964c62..6dfd63e 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -32,20 +32,16 @@ #define AUDIO_CAP "coreaudio" #include "audio_int.h" -struct { +static int isAtexit; + +typedef struct { int buffer_frames; int nbuffers; - int isAtexit; -} conf = { - .buffer_frames = 512, - .nbuffers = 4, - .isAtexit = 0 -}; +} CoreaudioConf; typedef struct coreaudioVoiceOut { HWVoiceOut hw; pthread_mutex_t mutex; - int isAtexit; AudioDeviceID outputDeviceID; UInt32 audioDevicePropertyBufferFrameSize; AudioStreamBasicDescription outputStreamBasicDescription; @@ -161,7 +157,7 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) static void coreaudio_atexit (void) { - conf.isAtexit = 1; + isAtexit = 1; } static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) @@ -287,7 +283,8 @@ static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) return audio_pcm_sw_write (sw, buf, len); } -static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) +static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) { OSStatus status; coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; @@ -295,6 +292,7 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) int err; const char *typ = "playback"; AudioValueRange frameRange; + CoreaudioConf *conf = drv_opaque; /* create mutex */ err = pthread_mutex_init(&core->mutex, NULL); @@ -336,16 +334,16 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) return -1; } - if (frameRange.mMinimum > conf.buffer_frames) { + if (frameRange.mMinimum > conf->buffer_frames) { core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); } - else if (frameRange.mMaximum < conf.buffer_frames) { + else if (frameRange.mMaximum < conf->buffer_frames) { core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); } else { - core->audioDevicePropertyBufferFrameSize = conf.buffer_frames; + core->audioDevicePropertyBufferFrameSize = conf->buffer_frames; } /* set Buffer Frame Size */ @@ -379,7 +377,7 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) "Could not get device buffer frame size\n"); return -1; } - hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize; + hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize; /* get StreamFormat */ propertySize = sizeof(core->outputStreamBasicDescription); @@ -443,7 +441,7 @@ static void coreaudio_fini_out (HWVoiceOut *hw) int err; coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; - if (!conf.isAtexit) { + if (!isAtexit) { /* stop playback */ if (isPlaying(core->outputDeviceID)) { status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); @@ -486,7 +484,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) case VOICE_DISABLE: /* stop playback */ - if (!conf.isAtexit) { + if (!isAtexit) { if (isPlaying(core->outputDeviceID)) { status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); if (status != kAudioHardwareNoError) { @@ -499,28 +497,36 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) return 0; } +static CoreaudioConf glob_conf = { + .buffer_frames = 512, + .nbuffers = 4, +}; + static void *coreaudio_audio_init (void) { + CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf)); + *conf = glob_conf; + atexit(coreaudio_atexit); - return &coreaudio_audio_init; + return conf; } static void coreaudio_audio_fini (void *opaque) { - (void) opaque; + g_free(opaque); } static struct audio_option coreaudio_options[] = { { .name = "BUFFER_SIZE", .tag = AUD_OPT_INT, - .valp = &conf.buffer_frames, + .valp = &glob_conf.buffer_frames, .descr = "Size of the buffer in frames" }, { .name = "BUFFER_COUNT", .tag = AUD_OPT_INT, - .valp = &conf.nbuffers, + .valp = &glob_conf.nbuffers, .descr = "Number of buffers" }, { /* End of list */ } diff --git a/audio/dsound_template.h b/audio/dsound_template.h index 8b37d16..b439f33 100644 --- a/audio/dsound_template.h +++ b/audio/dsound_template.h @@ -67,11 +67,11 @@ static int glue (dsound_lock_, TYPE) ( LPVOID *p2p, DWORD *blen1p, DWORD *blen2p, - int entire + int entire, + dsound *s ) { HRESULT hr; - int i; LPVOID p1 = NULL, p2 = NULL; DWORD blen1 = 0, blen2 = 0; DWORD flag; @@ -81,37 +81,18 @@ static int glue (dsound_lock_, TYPE) ( #else flag = entire ? DSBLOCK_ENTIREBUFFER : 0; #endif - for (i = 0; i < conf.lock_retries; ++i) { - hr = glue (IFACE, _Lock) ( - buf, - pos, - len, - &p1, - &blen1, - &p2, - &blen2, - flag - ); + hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag); - if (FAILED (hr)) { + if (FAILED (hr)) { #ifndef DSBTYPE_IN - if (hr == DSERR_BUFFERLOST) { - if (glue (dsound_restore_, TYPE) (buf)) { - dsound_logerr (hr, "Could not lock " NAME "\n"); - goto fail; - } - continue; + if (hr == DSERR_BUFFERLOST) { + if (glue (dsound_restore_, TYPE) (buf, s)) { + dsound_logerr (hr, "Could not lock " NAME "\n"); } -#endif - dsound_logerr (hr, "Could not lock " NAME "\n"); goto fail; } - - break; - } - - if (i == conf.lock_retries) { - dolog ("%d attempts to lock " NAME " failed\n", i); +#endif + dsound_logerr (hr, "Could not lock " NAME "\n"); goto fail; } @@ -174,16 +155,19 @@ static void dsound_fini_out (HWVoiceOut *hw) } #ifdef DSBTYPE_IN -static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as) +static int dsound_init_in(HWVoiceIn *hw, struct audsettings *as, + void *drv_opaque) #else -static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as) +static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) #endif { int err; HRESULT hr; - dsound *s = &glob_dsound; + dsound *s = drv_opaque; WAVEFORMATEX wfx; struct audsettings obt_as; + DSoundConf *conf = &s->conf; #ifdef DSBTYPE_IN const char *typ = "ADC"; DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; @@ -210,7 +194,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as) bd.dwSize = sizeof (bd); bd.lpwfxFormat = &wfx; #ifdef DSBTYPE_IN - bd.dwBufferBytes = conf.bufsize_in; + bd.dwBufferBytes = conf->bufsize_in; hr = IDirectSoundCapture_CreateCaptureBuffer ( s->dsound_capture, &bd, @@ -219,7 +203,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as) ); #else bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; - bd.dwBufferBytes = conf.bufsize_out; + bd.dwBufferBytes = conf->bufsize_out; hr = IDirectSound_CreateSoundBuffer ( s->dsound, &bd, @@ -269,6 +253,7 @@ static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as) ); } hw->samples = bc.dwBufferBytes >> hw->info.shift; + ds->s = s; #ifdef DEBUG_DSOUND dolog ("caps %ld, desc %ld\n", diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index e2d89fd..e9472c1 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -41,42 +41,25 @@ /* #define DEBUG_DSOUND */ -static struct { - int lock_retries; - int restore_retries; - int getstatus_retries; - int set_primary; +typedef struct { int bufsize_in; int bufsize_out; - struct audsettings settings; int latency_millis; -} conf = { - .lock_retries = 1, - .restore_retries = 1, - .getstatus_retries = 1, - .set_primary = 0, - .bufsize_in = 16384, - .bufsize_out = 16384, - .settings.freq = 44100, - .settings.nchannels = 2, - .settings.fmt = AUD_FMT_S16, - .latency_millis = 10 -}; +} DSoundConf; typedef struct { LPDIRECTSOUND dsound; LPDIRECTSOUNDCAPTURE dsound_capture; - LPDIRECTSOUNDBUFFER dsound_primary_buffer; struct audsettings settings; + DSoundConf conf; } dsound; -static dsound glob_dsound; - typedef struct { HWVoiceOut hw; LPDIRECTSOUNDBUFFER dsound_buffer; DWORD old_pos; int first_time; + dsound *s; #ifdef DEBUG_DSOUND DWORD old_ppos; DWORD played; @@ -88,6 +71,7 @@ typedef struct { HWVoiceIn hw; int first_time; LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; + dsound *s; } DSoundVoiceIn; static void dsound_log_hresult (HRESULT hr) @@ -281,29 +265,17 @@ static void print_wave_format (WAVEFORMATEX *wfx) } #endif -static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb) +static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb, dsound *s) { HRESULT hr; - int i; - - for (i = 0; i < conf.restore_retries; ++i) { - hr = IDirectSoundBuffer_Restore (dsb); - - switch (hr) { - case DS_OK: - return 0; - case DSERR_BUFFERLOST: - continue; + hr = IDirectSoundBuffer_Restore (dsb); - default: - dsound_logerr (hr, "Could not restore playback buffer\n"); - return -1; - } + if (hr != DS_OK) { + dsound_logerr (hr, "Could not restore playback buffer\n"); + return -1; } - - dolog ("%d attempts to restore playback buffer failed\n", i); - return -1; + return 0; } #include "dsound_template.h" @@ -311,25 +283,20 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb) #include "dsound_template.h" #undef DSBTYPE_IN -static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp) +static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp, + dsound *s) { HRESULT hr; - int i; - for (i = 0; i < conf.getstatus_retries; ++i) { - hr = IDirectSoundBuffer_GetStatus (dsb, statusp); - if (FAILED (hr)) { - dsound_logerr (hr, "Could not get playback buffer status\n"); - return -1; - } + hr = IDirectSoundBuffer_GetStatus (dsb, statusp); + if (FAILED (hr)) { + dsound_logerr (hr, "Could not get playback buffer status\n"); + return -1; + } - if (*statusp & DSERR_BUFFERLOST) { - if (dsound_restore_out (dsb)) { - return -1; - } - continue; - } - break; + if (*statusp & DSERR_BUFFERLOST) { + dsound_restore_out(dsb, s); + return -1; } return 0; @@ -376,7 +343,8 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) hw->rpos = pos % hw->samples; } -static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb) +static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, + dsound *s) { int err; LPVOID p1, p2; @@ -389,7 +357,8 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb) hw->samples << hw->info.shift, &p1, &p2, &blen1, &blen2, - 1 + 1, + s ); if (err) { return; @@ -415,25 +384,9 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb) dsound_unlock_out (dsb, p1, p2, blen1, blen2); } -static void dsound_close (dsound *s) -{ - HRESULT hr; - - if (s->dsound_primary_buffer) { - hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer); - if (FAILED (hr)) { - dsound_logerr (hr, "Could not release primary buffer\n"); - } - s->dsound_primary_buffer = NULL; - } -} - static int dsound_open (dsound *s) { - int err; HRESULT hr; - WAVEFORMATEX wfx; - DSBUFFERDESC dsbd; HWND hwnd; hwnd = GetForegroundWindow (); @@ -449,63 +402,7 @@ static int dsound_open (dsound *s) return -1; } - if (!conf.set_primary) { - return 0; - } - - err = waveformat_from_audio_settings (&wfx, &conf.settings); - if (err) { - return -1; - } - - memset (&dsbd, 0, sizeof (dsbd)); - dsbd.dwSize = sizeof (dsbd); - dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; - dsbd.dwBufferBytes = 0; - dsbd.lpwfxFormat = NULL; - - hr = IDirectSound_CreateSoundBuffer ( - s->dsound, - &dsbd, - &s->dsound_primary_buffer, - NULL - ); - if (FAILED (hr)) { - dsound_logerr (hr, "Could not create primary playback buffer\n"); - return -1; - } - - hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx); - if (FAILED (hr)) { - dsound_logerr (hr, "Could not set primary playback buffer format\n"); - } - - hr = IDirectSoundBuffer_GetFormat ( - s->dsound_primary_buffer, - &wfx, - sizeof (wfx), - NULL - ); - if (FAILED (hr)) { - dsound_logerr (hr, "Could not get primary playback buffer format\n"); - goto fail0; - } - -#ifdef DEBUG_DSOUND - dolog ("Primary\n"); - print_wave_format (&wfx); -#endif - - err = waveformat_to_audio_settings (&wfx, &s->settings); - if (err) { - goto fail0; - } - return 0; - - fail0: - dsound_close (s); - return -1; } static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) @@ -514,6 +411,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) DWORD status; DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; + dsound *s = ds->s; if (!dsb) { dolog ("Attempt to control voice without a buffer\n"); @@ -522,7 +420,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) switch (cmd) { case VOICE_ENABLE: - if (dsound_get_status_out (dsb, &status)) { + if (dsound_get_status_out (dsb, &status, s)) { return -1; } @@ -531,7 +429,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) return 0; } - dsound_clear_sample (hw, dsb); + dsound_clear_sample (hw, dsb, s); hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING); if (FAILED (hr)) { @@ -541,7 +439,7 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...) break; case VOICE_DISABLE: - if (dsound_get_status_out (dsb, &status)) { + if (dsound_get_status_out (dsb, &status, s)) { return -1; } @@ -578,6 +476,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live) DWORD wpos, ppos, old_pos; LPVOID p1, p2; int bufsize; + dsound *s = ds->s; + DSoundConf *conf = &s->conf; if (!dsb) { dolog ("Attempt to run empty with playback buffer\n"); @@ -600,14 +500,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live) len = live << hwshift; if (ds->first_time) { - if (conf.latency_millis) { + if (conf->latency_millis) { DWORD cur_blat; cur_blat = audio_ring_dist (wpos, ppos, bufsize); ds->first_time = 0; old_pos = wpos; old_pos += - millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat; + millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat; old_pos %= bufsize; old_pos &= ~hw->info.align; } @@ -663,7 +563,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live) len, &p1, &p2, &blen1, &blen2, - 0 + 0, + s ); if (err) { return 0; @@ -766,6 +667,7 @@ static int dsound_run_in (HWVoiceIn *hw) DWORD cpos, rpos; LPVOID p1, p2; int hwshift; + dsound *s = ds->s; if (!dscb) { dolog ("Attempt to run without capture buffer\n"); @@ -820,7 +722,8 @@ static int dsound_run_in (HWVoiceIn *hw) &p2, &blen1, &blen2, - 0 + 0, + s ); if (err) { return 0; @@ -843,12 +746,19 @@ static int dsound_run_in (HWVoiceIn *hw) return decr; } +static DSoundConf glob_conf = { + .bufsize_in = 16384, + .bufsize_out = 16384, + .latency_millis = 10 +}; + static void dsound_audio_fini (void *opaque) { HRESULT hr; dsound *s = opaque; if (!s->dsound) { + g_free(s); return; } @@ -859,6 +769,7 @@ static void dsound_audio_fini (void *opaque) s->dsound = NULL; if (!s->dsound_capture) { + g_free(s); return; } @@ -867,17 +778,21 @@ static void dsound_audio_fini (void *opaque) dsound_logerr (hr, "Could not release DirectSoundCapture\n"); } s->dsound_capture = NULL; + + g_free(s); } static void *dsound_audio_init (void) { int err; HRESULT hr; - dsound *s = &glob_dsound; + dsound *s = g_malloc0(sizeof(dsound)); + s->conf = glob_conf; hr = CoInitialize (NULL); if (FAILED (hr)) { dsound_logerr (hr, "Could not initialize COM\n"); + g_free(s); return NULL; } @@ -890,6 +805,7 @@ static void *dsound_audio_init (void) ); if (FAILED (hr)) { dsound_logerr (hr, "Could not create DirectSound instance\n"); + g_free(s); return NULL; } @@ -901,7 +817,7 @@ static void *dsound_audio_init (void) if (FAILED (hr)) { dsound_logerr (hr, "Could not release DirectSound\n"); } - s->dsound = NULL; + g_free(s); return NULL; } @@ -939,63 +855,21 @@ static void *dsound_audio_init (void) static struct audio_option dsound_options[] = { { - .name = "LOCK_RETRIES", - .tag = AUD_OPT_INT, - .valp = &conf.lock_retries, - .descr = "Number of times to attempt locking the buffer" - }, - { - .name = "RESTOURE_RETRIES", - .tag = AUD_OPT_INT, - .valp = &conf.restore_retries, - .descr = "Number of times to attempt restoring the buffer" - }, - { - .name = "GETSTATUS_RETRIES", - .tag = AUD_OPT_INT, - .valp = &conf.getstatus_retries, - .descr = "Number of times to attempt getting status of the buffer" - }, - { - .name = "SET_PRIMARY", - .tag = AUD_OPT_BOOL, - .valp = &conf.set_primary, - .descr = "Set the parameters of primary buffer" - }, - { .name = "LATENCY_MILLIS", .tag = AUD_OPT_INT, - .valp = &conf.latency_millis, + .valp = &glob_conf.latency_millis, .descr = "(undocumented)" }, { - .name = "PRIMARY_FREQ", - .tag = AUD_OPT_INT, - .valp = &conf.settings.freq, - .descr = "Primary buffer frequency" - }, - { - .name = "PRIMARY_CHANNELS", - .tag = AUD_OPT_INT, - .valp = &conf.settings.nchannels, - .descr = "Primary buffer number of channels (1 - mono, 2 - stereo)" - }, - { - .name = "PRIMARY_FMT", - .tag = AUD_OPT_FMT, - .valp = &conf.settings.fmt, - .descr = "Primary buffer format" - }, - { .name = "BUFSIZE_OUT", .tag = AUD_OPT_INT, - .valp = &conf.bufsize_out, + .valp = &glob_conf.bufsize_out, .descr = "(undocumented)" }, { .name = "BUFSIZE_IN", .tag = AUD_OPT_INT, - .valp = &conf.bufsize_in, + .valp = &glob_conf.bufsize_in, .descr = "(undocumented)" }, { /* End of list */ } diff --git a/audio/esdaudio.c b/audio/esdaudio.c deleted file mode 100644 index eea9cce..0000000 --- a/audio/esdaudio.c +++ /dev/null @@ -1,557 +0,0 @@ -/* - * QEMU ESD audio driver - * - * Copyright (c) 2006 Frederick Reeve (brushed up by malc) - * - * 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 <esd.h> -#include "qemu-common.h" -#include "audio.h" - -#define AUDIO_CAP "esd" -#include "audio_int.h" -#include "audio_pt_int.h" - -typedef struct { - HWVoiceOut hw; - int done; - int live; - int decr; - int rpos; - void *pcm_buf; - int fd; - struct audio_pt pt; -} ESDVoiceOut; - -typedef struct { - HWVoiceIn hw; - int done; - int dead; - int incr; - int wpos; - void *pcm_buf; - int fd; - struct audio_pt pt; -} ESDVoiceIn; - -static struct { - int samples; - int divisor; - char *dac_host; - char *adc_host; -} conf = { - .samples = 1024, - .divisor = 2, -}; - -static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...) -{ - va_list ap; - - va_start (ap, fmt); - AUD_vlog (AUDIO_CAP, fmt, ap); - va_end (ap); - - AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); -} - -/* playback */ -static void *qesd_thread_out (void *arg) -{ - ESDVoiceOut *esd = arg; - HWVoiceOut *hw = &esd->hw; - int threshold; - - threshold = conf.divisor ? hw->samples / conf.divisor : 0; - - if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { - return NULL; - } - - for (;;) { - int decr, to_mix, rpos; - - for (;;) { - if (esd->done) { - goto exit; - } - - if (esd->live > threshold) { - break; - } - - if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) { - goto exit; - } - } - - decr = to_mix = esd->live; - rpos = hw->rpos; - - if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) { - return NULL; - } - - while (to_mix) { - ssize_t written; - int chunk = audio_MIN (to_mix, hw->samples - rpos); - struct st_sample *src = hw->mix_buf + rpos; - - hw->clip (esd->pcm_buf, src, chunk); - - again: - written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift); - if (written == -1) { - if (errno == EINTR || errno == EAGAIN) { - goto again; - } - qesd_logerr (errno, "write failed\n"); - return NULL; - } - - if (written != chunk << hw->info.shift) { - int wsamples = written >> hw->info.shift; - int wbytes = wsamples << hw->info.shift; - if (wbytes != written) { - dolog ("warning: Misaligned write %d (requested %zd), " - "alignment %d\n", - wbytes, written, hw->info.align + 1); - } - to_mix -= wsamples; - rpos = (rpos + wsamples) % hw->samples; - break; - } - - rpos = (rpos + chunk) % hw->samples; - to_mix -= chunk; - } - - if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { - return NULL; - } - - esd->rpos = rpos; - esd->live -= decr; - esd->decr += decr; - } - - exit: - audio_pt_unlock (&esd->pt, AUDIO_FUNC); - return NULL; -} - -static int qesd_run_out (HWVoiceOut *hw, int live) -{ - int decr; - ESDVoiceOut *esd = (ESDVoiceOut *) hw; - - if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { - return 0; - } - - decr = audio_MIN (live, esd->decr); - esd->decr -= decr; - esd->live = live - decr; - hw->rpos = esd->rpos; - if (esd->live > 0) { - audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); - } - else { - audio_pt_unlock (&esd->pt, AUDIO_FUNC); - } - return decr; -} - -static int qesd_write (SWVoiceOut *sw, void *buf, int len) -{ - return audio_pcm_sw_write (sw, buf, len); -} - -static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) -{ - ESDVoiceOut *esd = (ESDVoiceOut *) hw; - struct audsettings obt_as = *as; - int esdfmt = ESD_STREAM | ESD_PLAY; - - esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; - switch (as->fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: - esdfmt |= ESD_BITS8; - obt_as.fmt = AUD_FMT_U8; - break; - - case AUD_FMT_S32: - case AUD_FMT_U32: - dolog ("Will use 16 instead of 32 bit samples\n"); - /* fall through */ - case AUD_FMT_S16: - case AUD_FMT_U16: - deffmt: - esdfmt |= ESD_BITS16; - obt_as.fmt = AUD_FMT_S16; - break; - - default: - dolog ("Internal logic error: Bad audio format %d\n", as->fmt); - goto deffmt; - - } - obt_as.endianness = AUDIO_HOST_ENDIANNESS; - - audio_pcm_init_info (&hw->info, &obt_as); - - hw->samples = conf.samples; - esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); - if (!esd->pcm_buf) { - dolog ("Could not allocate buffer (%d bytes)\n", - hw->samples << hw->info.shift); - return -1; - } - - esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL); - if (esd->fd < 0) { - qesd_logerr (errno, "esd_play_stream failed\n"); - goto fail1; - } - - if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) { - goto fail2; - } - - return 0; - - fail2: - if (close (esd->fd)) { - qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", - AUDIO_FUNC, esd->fd); - } - esd->fd = -1; - - fail1: - g_free (esd->pcm_buf); - esd->pcm_buf = NULL; - return -1; -} - -static void qesd_fini_out (HWVoiceOut *hw) -{ - void *ret; - ESDVoiceOut *esd = (ESDVoiceOut *) hw; - - audio_pt_lock (&esd->pt, AUDIO_FUNC); - esd->done = 1; - audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); - audio_pt_join (&esd->pt, &ret, AUDIO_FUNC); - - if (esd->fd >= 0) { - if (close (esd->fd)) { - qesd_logerr (errno, "failed to close esd socket\n"); - } - esd->fd = -1; - } - - audio_pt_fini (&esd->pt, AUDIO_FUNC); - - g_free (esd->pcm_buf); - esd->pcm_buf = NULL; -} - -static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...) -{ - (void) hw; - (void) cmd; - return 0; -} - -/* capture */ -static void *qesd_thread_in (void *arg) -{ - ESDVoiceIn *esd = arg; - HWVoiceIn *hw = &esd->hw; - int threshold; - - threshold = conf.divisor ? hw->samples / conf.divisor : 0; - - if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { - return NULL; - } - - for (;;) { - int incr, to_grab, wpos; - - for (;;) { - if (esd->done) { - goto exit; - } - - if (esd->dead > threshold) { - break; - } - - if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) { - goto exit; - } - } - - incr = to_grab = esd->dead; - wpos = hw->wpos; - - if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) { - return NULL; - } - - while (to_grab) { - ssize_t nread; - int chunk = audio_MIN (to_grab, hw->samples - wpos); - void *buf = advance (esd->pcm_buf, wpos); - - again: - nread = read (esd->fd, buf, chunk << hw->info.shift); - if (nread == -1) { - if (errno == EINTR || errno == EAGAIN) { - goto again; - } - qesd_logerr (errno, "read failed\n"); - return NULL; - } - - if (nread != chunk << hw->info.shift) { - int rsamples = nread >> hw->info.shift; - int rbytes = rsamples << hw->info.shift; - if (rbytes != nread) { - dolog ("warning: Misaligned write %d (requested %zd), " - "alignment %d\n", - rbytes, nread, hw->info.align + 1); - } - to_grab -= rsamples; - wpos = (wpos + rsamples) % hw->samples; - break; - } - - hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift); - wpos = (wpos + chunk) % hw->samples; - to_grab -= chunk; - } - - if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { - return NULL; - } - - esd->wpos = wpos; - esd->dead -= incr; - esd->incr += incr; - } - - exit: - audio_pt_unlock (&esd->pt, AUDIO_FUNC); - return NULL; -} - -static int qesd_run_in (HWVoiceIn *hw) -{ - int live, incr, dead; - ESDVoiceIn *esd = (ESDVoiceIn *) hw; - - if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { - return 0; - } - - live = audio_pcm_hw_get_live_in (hw); - dead = hw->samples - live; - incr = audio_MIN (dead, esd->incr); - esd->incr -= incr; - esd->dead = dead - incr; - hw->wpos = esd->wpos; - if (esd->dead > 0) { - audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); - } - else { - audio_pt_unlock (&esd->pt, AUDIO_FUNC); - } - return incr; -} - -static int qesd_read (SWVoiceIn *sw, void *buf, int len) -{ - return audio_pcm_sw_read (sw, buf, len); -} - -static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as) -{ - ESDVoiceIn *esd = (ESDVoiceIn *) hw; - struct audsettings obt_as = *as; - int esdfmt = ESD_STREAM | ESD_RECORD; - - esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; - switch (as->fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: - esdfmt |= ESD_BITS8; - obt_as.fmt = AUD_FMT_U8; - break; - - case AUD_FMT_S16: - case AUD_FMT_U16: - esdfmt |= ESD_BITS16; - obt_as.fmt = AUD_FMT_S16; - break; - - case AUD_FMT_S32: - case AUD_FMT_U32: - dolog ("Will use 16 instead of 32 bit samples\n"); - esdfmt |= ESD_BITS16; - obt_as.fmt = AUD_FMT_S16; - break; - } - obt_as.endianness = AUDIO_HOST_ENDIANNESS; - - audio_pcm_init_info (&hw->info, &obt_as); - - hw->samples = conf.samples; - esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); - if (!esd->pcm_buf) { - dolog ("Could not allocate buffer (%d bytes)\n", - hw->samples << hw->info.shift); - return -1; - } - - esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL); - if (esd->fd < 0) { - qesd_logerr (errno, "esd_record_stream failed\n"); - goto fail1; - } - - if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) { - goto fail2; - } - - return 0; - - fail2: - if (close (esd->fd)) { - qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", - AUDIO_FUNC, esd->fd); - } - esd->fd = -1; - - fail1: - g_free (esd->pcm_buf); - esd->pcm_buf = NULL; - return -1; -} - -static void qesd_fini_in (HWVoiceIn *hw) -{ - void *ret; - ESDVoiceIn *esd = (ESDVoiceIn *) hw; - - audio_pt_lock (&esd->pt, AUDIO_FUNC); - esd->done = 1; - audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); - audio_pt_join (&esd->pt, &ret, AUDIO_FUNC); - - if (esd->fd >= 0) { - if (close (esd->fd)) { - qesd_logerr (errno, "failed to close esd socket\n"); - } - esd->fd = -1; - } - - audio_pt_fini (&esd->pt, AUDIO_FUNC); - - g_free (esd->pcm_buf); - esd->pcm_buf = NULL; -} - -static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...) -{ - (void) hw; - (void) cmd; - return 0; -} - -/* common */ -static void *qesd_audio_init (void) -{ - return &conf; -} - -static void qesd_audio_fini (void *opaque) -{ - (void) opaque; - ldebug ("esd_fini"); -} - -struct audio_option qesd_options[] = { - { - .name = "SAMPLES", - .tag = AUD_OPT_INT, - .valp = &conf.samples, - .descr = "buffer size in samples" - }, - { - .name = "DIVISOR", - .tag = AUD_OPT_INT, - .valp = &conf.divisor, - .descr = "threshold divisor" - }, - { - .name = "DAC_HOST", - .tag = AUD_OPT_STR, - .valp = &conf.dac_host, - .descr = "playback host" - }, - { - .name = "ADC_HOST", - .tag = AUD_OPT_STR, - .valp = &conf.adc_host, - .descr = "capture host" - }, - { /* End of list */ } -}; - -static struct audio_pcm_ops qesd_pcm_ops = { - .init_out = qesd_init_out, - .fini_out = qesd_fini_out, - .run_out = qesd_run_out, - .write = qesd_write, - .ctl_out = qesd_ctl_out, - - .init_in = qesd_init_in, - .fini_in = qesd_fini_in, - .run_in = qesd_run_in, - .read = qesd_read, - .ctl_in = qesd_ctl_in, -}; - -struct audio_driver esd_audio_driver = { - .name = "esd", - .descr = "http://en.wikipedia.org/wiki/Esound", - .options = qesd_options, - .init = qesd_audio_init, - .fini = qesd_audio_fini, - .pcm_ops = &qesd_pcm_ops, - .can_be_default = 0, - .max_voices_out = INT_MAX, - .max_voices_in = INT_MAX, - .voice_size_out = sizeof (ESDVoiceOut), - .voice_size_in = sizeof (ESDVoiceIn) -}; diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c deleted file mode 100644 index fabf84d..0000000 --- a/audio/fmodaudio.c +++ /dev/null @@ -1,685 +0,0 @@ -/* - * QEMU FMOD audio driver - * - * Copyright (c) 2004-2005 Vassili Karpov (malc) - * - * 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 <fmod.h> -#include <fmod_errors.h> -#include "qemu-common.h" -#include "audio.h" - -#define AUDIO_CAP "fmod" -#include "audio_int.h" - -typedef struct FMODVoiceOut { - HWVoiceOut hw; - unsigned int old_pos; - FSOUND_SAMPLE *fmod_sample; - int channel; -} FMODVoiceOut; - -typedef struct FMODVoiceIn { - HWVoiceIn hw; - FSOUND_SAMPLE *fmod_sample; -} FMODVoiceIn; - -static struct { - const char *drvname; - int nb_samples; - int freq; - int nb_channels; - int bufsize; - int broken_adc; -} conf = { - .nb_samples = 2048 * 2, - .freq = 44100, - .nb_channels = 2, -}; - -static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...) -{ - va_list ap; - - va_start (ap, fmt); - AUD_vlog (AUDIO_CAP, fmt, ap); - va_end (ap); - - AUD_log (AUDIO_CAP, "Reason: %s\n", - FMOD_ErrorString (FSOUND_GetError ())); -} - -static void GCC_FMT_ATTR (2, 3) fmod_logerr2 ( - const char *typ, - const char *fmt, - ... - ) -{ - va_list ap; - - AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); - - va_start (ap, fmt); - AUD_vlog (AUDIO_CAP, fmt, ap); - va_end (ap); - - AUD_log (AUDIO_CAP, "Reason: %s\n", - FMOD_ErrorString (FSOUND_GetError ())); -} - -static int fmod_write (SWVoiceOut *sw, void *buf, int len) -{ - return audio_pcm_sw_write (sw, buf, len); -} - -static void fmod_clear_sample (FMODVoiceOut *fmd) -{ - HWVoiceOut *hw = &fmd->hw; - int status; - void *p1 = 0, *p2 = 0; - unsigned int len1 = 0, len2 = 0; - - status = FSOUND_Sample_Lock ( - fmd->fmod_sample, - 0, - hw->samples << hw->info.shift, - &p1, - &p2, - &len1, - &len2 - ); - - if (!status) { - fmod_logerr ("Failed to lock sample\n"); - return; - } - - if ((len1 & hw->info.align) || (len2 & hw->info.align)) { - dolog ("Lock returned misaligned length %d, %d, alignment %d\n", - len1, len2, hw->info.align + 1); - goto fail; - } - - if ((len1 + len2) - (hw->samples << hw->info.shift)) { - dolog ("Lock returned incomplete length %d, %d\n", - len1 + len2, hw->samples << hw->info.shift); - goto fail; - } - - audio_pcm_info_clear_buf (&hw->info, p1, hw->samples); - - fail: - status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); - if (!status) { - fmod_logerr ("Failed to unlock sample\n"); - } -} - -static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) -{ - int src_len1 = dst_len; - int src_len2 = 0; - int pos = hw->rpos + dst_len; - struct st_sample *src1 = hw->mix_buf + hw->rpos; - struct st_sample *src2 = NULL; - - if (pos > hw->samples) { - src_len1 = hw->samples - hw->rpos; - src2 = hw->mix_buf; - src_len2 = dst_len - src_len1; - pos = src_len2; - } - - if (src_len1) { - hw->clip (dst, src1, src_len1); - } - - if (src_len2) { - dst = advance (dst, src_len1 << hw->info.shift); - hw->clip (dst, src2, src_len2); - } - - hw->rpos = pos % hw->samples; -} - -static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2, - unsigned int blen1, unsigned int blen2) -{ - int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2); - if (!status) { - fmod_logerr ("Failed to unlock sample\n"); - return -1; - } - return 0; -} - -static int fmod_lock_sample ( - FSOUND_SAMPLE *sample, - struct audio_pcm_info *info, - int pos, - int len, - void **p1, - void **p2, - unsigned int *blen1, - unsigned int *blen2 - ) -{ - int status; - - status = FSOUND_Sample_Lock ( - sample, - pos << info->shift, - len << info->shift, - p1, - p2, - blen1, - blen2 - ); - - if (!status) { - fmod_logerr ("Failed to lock sample\n"); - return -1; - } - - if ((*blen1 & info->align) || (*blen2 & info->align)) { - dolog ("Lock returned misaligned length %d, %d, alignment %d\n", - *blen1, *blen2, info->align + 1); - - fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2); - - *p1 = NULL - 1; - *p2 = NULL - 1; - *blen1 = ~0U; - *blen2 = ~0U; - return -1; - } - - if (!*p1 && *blen1) { - dolog ("warning: !p1 && blen1=%d\n", *blen1); - *blen1 = 0; - } - - if (!p2 && *blen2) { - dolog ("warning: !p2 && blen2=%d\n", *blen2); - *blen2 = 0; - } - - return 0; -} - -static int fmod_run_out (HWVoiceOut *hw, int live) -{ - FMODVoiceOut *fmd = (FMODVoiceOut *) hw; - int decr; - void *p1 = 0, *p2 = 0; - unsigned int blen1 = 0, blen2 = 0; - unsigned int len1 = 0, len2 = 0; - - if (!hw->pending_disable) { - return 0; - } - - decr = live; - - if (fmd->channel >= 0) { - int len = decr; - int old_pos = fmd->old_pos; - int ppos = FSOUND_GetCurrentPosition (fmd->channel); - - if (ppos == old_pos || !ppos) { - return 0; - } - - if ((old_pos < ppos) && ((old_pos + len) > ppos)) { - len = ppos - old_pos; - } - else { - if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) { - len = hw->samples - old_pos + ppos; - } - } - decr = len; - - if (audio_bug (AUDIO_FUNC, decr < 0)) { - dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n", - decr, live, ppos, old_pos, len); - return 0; - } - } - - - if (!decr) { - return 0; - } - - if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, - fmd->old_pos, decr, - &p1, &p2, - &blen1, &blen2)) { - return 0; - } - - len1 = blen1 >> hw->info.shift; - len2 = blen2 >> hw->info.shift; - ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2); - decr = len1 + len2; - - if (p1 && len1) { - fmod_write_sample (hw, p1, len1); - } - - if (p2 && len2) { - fmod_write_sample (hw, p2, len2); - } - - fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); - - fmd->old_pos = (fmd->old_pos + decr) % hw->samples; - return decr; -} - -static int aud_to_fmodfmt (audfmt_e fmt, int stereo) -{ - int mode = FSOUND_LOOP_NORMAL; - - switch (fmt) { - case AUD_FMT_S8: - mode |= FSOUND_SIGNED | FSOUND_8BITS; - break; - - case AUD_FMT_U8: - mode |= FSOUND_UNSIGNED | FSOUND_8BITS; - break; - - case AUD_FMT_S16: - mode |= FSOUND_SIGNED | FSOUND_16BITS; - break; - - case AUD_FMT_U16: - mode |= FSOUND_UNSIGNED | FSOUND_16BITS; - break; - - default: - dolog ("Internal logic error: Bad audio format %d\n", fmt); -#ifdef DEBUG_FMOD - abort (); -#endif - mode |= FSOUND_8BITS; - } - mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; - return mode; -} - -static void fmod_fini_out (HWVoiceOut *hw) -{ - FMODVoiceOut *fmd = (FMODVoiceOut *) hw; - - if (fmd->fmod_sample) { - FSOUND_Sample_Free (fmd->fmod_sample); - fmd->fmod_sample = 0; - - if (fmd->channel >= 0) { - FSOUND_StopSound (fmd->channel); - } - } -} - -static int fmod_init_out (HWVoiceOut *hw, struct audsettings *as) -{ - int mode, channel; - FMODVoiceOut *fmd = (FMODVoiceOut *) hw; - struct audsettings obt_as = *as; - - mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); - fmd->fmod_sample = FSOUND_Sample_Alloc ( - FSOUND_FREE, /* index */ - conf.nb_samples, /* length */ - mode, /* mode */ - as->freq, /* freq */ - 255, /* volume */ - 128, /* pan */ - 255 /* priority */ - ); - - if (!fmd->fmod_sample) { - fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n"); - return -1; - } - - channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); - if (channel < 0) { - fmod_logerr2 ("DAC", "Failed to start playing sound\n"); - FSOUND_Sample_Free (fmd->fmod_sample); - return -1; - } - fmd->channel = channel; - - /* FMOD always operates on little endian frames? */ - obt_as.endianness = 0; - audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = conf.nb_samples; - return 0; -} - -static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...) -{ - int status; - FMODVoiceOut *fmd = (FMODVoiceOut *) hw; - - switch (cmd) { - case VOICE_ENABLE: - fmod_clear_sample (fmd); - status = FSOUND_SetPaused (fmd->channel, 0); - if (!status) { - fmod_logerr ("Failed to resume channel %d\n", fmd->channel); - } - break; - - case VOICE_DISABLE: - status = FSOUND_SetPaused (fmd->channel, 1); - if (!status) { - fmod_logerr ("Failed to pause channel %d\n", fmd->channel); - } - break; - } - return 0; -} - -static int fmod_init_in (HWVoiceIn *hw, struct audsettings *as) -{ - int mode; - FMODVoiceIn *fmd = (FMODVoiceIn *) hw; - struct audsettings obt_as = *as; - - if (conf.broken_adc) { - return -1; - } - - mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); - fmd->fmod_sample = FSOUND_Sample_Alloc ( - FSOUND_FREE, /* index */ - conf.nb_samples, /* length */ - mode, /* mode */ - as->freq, /* freq */ - 255, /* volume */ - 128, /* pan */ - 255 /* priority */ - ); - - if (!fmd->fmod_sample) { - fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n"); - return -1; - } - - /* FMOD always operates on little endian frames? */ - obt_as.endianness = 0; - audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = conf.nb_samples; - return 0; -} - -static void fmod_fini_in (HWVoiceIn *hw) -{ - FMODVoiceIn *fmd = (FMODVoiceIn *) hw; - - if (fmd->fmod_sample) { - FSOUND_Record_Stop (); - FSOUND_Sample_Free (fmd->fmod_sample); - fmd->fmod_sample = 0; - } -} - -static int fmod_run_in (HWVoiceIn *hw) -{ - FMODVoiceIn *fmd = (FMODVoiceIn *) hw; - int hwshift = hw->info.shift; - int live, dead, new_pos, len; - unsigned int blen1 = 0, blen2 = 0; - unsigned int len1, len2; - unsigned int decr; - void *p1, *p2; - - live = audio_pcm_hw_get_live_in (hw); - dead = hw->samples - live; - if (!dead) { - return 0; - } - - new_pos = FSOUND_Record_GetPosition (); - if (new_pos < 0) { - fmod_logerr ("Could not get recording position\n"); - return 0; - } - - len = audio_ring_dist (new_pos, hw->wpos, hw->samples); - if (!len) { - return 0; - } - len = audio_MIN (len, dead); - - if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info, - hw->wpos, len, - &p1, &p2, - &blen1, &blen2)) { - return 0; - } - - len1 = blen1 >> hwshift; - len2 = blen2 >> hwshift; - decr = len1 + len2; - - if (p1 && blen1) { - hw->conv (hw->conv_buf + hw->wpos, p1, len1); - } - if (p2 && len2) { - hw->conv (hw->conv_buf, p2, len2); - } - - fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); - hw->wpos = (hw->wpos + decr) % hw->samples; - return decr; -} - -static struct { - const char *name; - int type; -} drvtab[] = { - { .name = "none", .type = FSOUND_OUTPUT_NOSOUND }, -#ifdef _WIN32 - { .name = "winmm", .type = FSOUND_OUTPUT_WINMM }, - { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND }, - { .name = "a3d", .type = FSOUND_OUTPUT_A3D }, - { .name = "asio", .type = FSOUND_OUTPUT_ASIO }, -#endif -#ifdef __linux__ - { .name = "oss", .type = FSOUND_OUTPUT_OSS }, - { .name = "alsa", .type = FSOUND_OUTPUT_ALSA }, - { .name = "esd", .type = FSOUND_OUTPUT_ESD }, -#endif -#ifdef __APPLE__ - { .name = "mac", .type = FSOUND_OUTPUT_MAC }, -#endif -#if 0 - { .name = "xbox", .type = FSOUND_OUTPUT_XBOX }, - { .name = "ps2", .type = FSOUND_OUTPUT_PS2 }, - { .name = "gcube", .type = FSOUND_OUTPUT_GC }, -#endif - { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME } -}; - -static void *fmod_audio_init (void) -{ - size_t i; - double ver; - int status; - int output_type = -1; - const char *drv = conf.drvname; - - ver = FSOUND_GetVersion (); - if (ver < FMOD_VERSION) { - dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION); - return NULL; - } - -#ifdef __linux__ - if (ver < 3.75) { - dolog ("FMOD before 3.75 has bug preventing ADC from working\n" - "ADC will be disabled.\n"); - conf.broken_adc = 1; - } -#endif - - if (drv) { - int found = 0; - for (i = 0; i < ARRAY_SIZE (drvtab); i++) { - if (!strcmp (drv, drvtab[i].name)) { - output_type = drvtab[i].type; - found = 1; - break; - } - } - if (!found) { - dolog ("Unknown FMOD driver `%s'\n", drv); - dolog ("Valid drivers:\n"); - for (i = 0; i < ARRAY_SIZE (drvtab); i++) { - dolog (" %s\n", drvtab[i].name); - } - } - } - - if (output_type != -1) { - status = FSOUND_SetOutput (output_type); - if (!status) { - fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type); - return NULL; - } - } - - if (conf.bufsize) { - status = FSOUND_SetBufferSize (conf.bufsize); - if (!status) { - fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize); - } - } - - status = FSOUND_Init (conf.freq, conf.nb_channels, 0); - if (!status) { - fmod_logerr ("FSOUND_Init failed\n"); - return NULL; - } - - return &conf; -} - -static int fmod_read (SWVoiceIn *sw, void *buf, int size) -{ - return audio_pcm_sw_read (sw, buf, size); -} - -static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...) -{ - int status; - FMODVoiceIn *fmd = (FMODVoiceIn *) hw; - - switch (cmd) { - case VOICE_ENABLE: - status = FSOUND_Record_StartSample (fmd->fmod_sample, 1); - if (!status) { - fmod_logerr ("Failed to start recording\n"); - } - break; - - case VOICE_DISABLE: - status = FSOUND_Record_Stop (); - if (!status) { - fmod_logerr ("Failed to stop recording\n"); - } - break; - } - return 0; -} - -static void fmod_audio_fini (void *opaque) -{ - (void) opaque; - FSOUND_Close (); -} - -static struct audio_option fmod_options[] = { - { - .name = "DRV", - .tag = AUD_OPT_STR, - .valp = &conf.drvname, - .descr = "FMOD driver" - }, - { - .name = "FREQ", - .tag = AUD_OPT_INT, - .valp = &conf.freq, - .descr = "Default frequency" - }, - { - .name = "SAMPLES", - .tag = AUD_OPT_INT, - .valp = &conf.nb_samples, - .descr = "Buffer size in samples" - }, - { - .name = "CHANNELS", - .tag = AUD_OPT_INT, - .valp = &conf.nb_channels, - .descr = "Number of default channels (1 - mono, 2 - stereo)" - }, - { - .name = "BUFSIZE", - .tag = AUD_OPT_INT, - .valp = &conf.bufsize, - .descr = "(undocumented)" - }, - { /* End of list */ } -}; - -static struct audio_pcm_ops fmod_pcm_ops = { - .init_out = fmod_init_out, - .fini_out = fmod_fini_out, - .run_out = fmod_run_out, - .write = fmod_write, - .ctl_out = fmod_ctl_out, - - .init_in = fmod_init_in, - .fini_in = fmod_fini_in, - .run_in = fmod_run_in, - .read = fmod_read, - .ctl_in = fmod_ctl_in -}; - -struct audio_driver fmod_audio_driver = { - .name = "fmod", - .descr = "FMOD 3.xx http://www.fmod.org", - .options = fmod_options, - .init = fmod_audio_init, - .fini = fmod_audio_fini, - .pcm_ops = &fmod_pcm_ops, - .can_be_default = 1, - .max_voices_out = INT_MAX, - .max_voices_in = INT_MAX, - .voice_size_out = sizeof (FMODVoiceOut), - .voice_size_in = sizeof (FMODVoiceIn) -}; diff --git a/audio/noaudio.c b/audio/noaudio.c index cb38662..50db1f3 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -63,7 +63,7 @@ static int no_write (SWVoiceOut *sw, void *buf, int len) return audio_pcm_sw_write (sw, buf, len); } -static int no_init_out (HWVoiceOut *hw, struct audsettings *as) +static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) { audio_pcm_init_info (&hw->info, as); hw->samples = 1024; @@ -82,7 +82,7 @@ static int no_ctl_out (HWVoiceOut *hw, int cmd, ...) return 0; } -static int no_init_in (HWVoiceIn *hw, struct audsettings *as) +static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) { audio_pcm_init_info (&hw->info, as); hw->samples = 1024; diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 4db2ca6..11e76a1 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -30,6 +30,7 @@ #include "qemu/main-loop.h" #include "qemu/host-utils.h" #include "audio.h" +#include "trace.h" #define AUDIO_CAP "oss" #include "audio_int.h" @@ -38,6 +39,16 @@ #define USE_DSP_POLICY #endif +typedef struct OSSConf { + int try_mmap; + int nfrags; + int fragsize; + const char *devpath_out; + const char *devpath_in; + int exclusive; + int policy; +} OSSConf; + typedef struct OSSVoiceOut { HWVoiceOut hw; void *pcm_buf; @@ -47,6 +58,7 @@ typedef struct OSSVoiceOut { int fragsize; int mmapped; int pending; + OSSConf *conf; } OSSVoiceOut; typedef struct OSSVoiceIn { @@ -55,28 +67,9 @@ typedef struct OSSVoiceIn { int fd; int nfrags; int fragsize; + OSSConf *conf; } OSSVoiceIn; -static struct { - int try_mmap; - int nfrags; - int fragsize; - const char *devpath_out; - const char *devpath_in; - int debug; - int exclusive; - int policy; -} conf = { - .try_mmap = 0, - .nfrags = 4, - .fragsize = 4096, - .devpath_out = "/dev/dsp", - .devpath_in = "/dev/dsp", - .debug = 0, - .exclusive = 0, - .policy = 5 -}; - struct oss_params { int freq; audfmt_e fmt; @@ -138,18 +131,18 @@ static void oss_helper_poll_in (void *opaque) audio_run ("oss_poll_in"); } -static int oss_poll_out (HWVoiceOut *hw) +static void oss_poll_out (HWVoiceOut *hw) { OSSVoiceOut *oss = (OSSVoiceOut *) hw; - return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); + qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); } -static int oss_poll_in (HWVoiceIn *hw) +static void oss_poll_in (HWVoiceIn *hw) { OSSVoiceIn *oss = (OSSVoiceIn *) hw; - return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); + qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); } static int oss_write (SWVoiceOut *sw, void *buf, int len) @@ -272,18 +265,18 @@ static int oss_get_version (int fd, int *version, const char *typ) #endif static int oss_open (int in, struct oss_params *req, - struct oss_params *obt, int *pfd) + struct oss_params *obt, int *pfd, OSSConf* conf) { int fd; - int oflags = conf.exclusive ? O_EXCL : 0; + int oflags = conf->exclusive ? O_EXCL : 0; audio_buf_info abinfo; int fmt, freq, nchannels; int setfragment = 1; - const char *dspname = in ? conf.devpath_in : conf.devpath_out; + const char *dspname = in ? conf->devpath_in : conf->devpath_out; const char *typ = in ? "ADC" : "DAC"; /* Kludge needed to have working mmap on Linux */ - oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); + oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); fd = open (dspname, oflags | O_NONBLOCK); if (-1 == fd) { @@ -317,20 +310,18 @@ static int oss_open (int in, struct oss_params *req, } #ifdef USE_DSP_POLICY - if (conf.policy >= 0) { + if (conf->policy >= 0) { int version; if (!oss_get_version (fd, &version, typ)) { - if (conf.debug) { - dolog ("OSS version = %#x\n", version); - } + trace_oss_version(version); if (version >= 0x040000) { - int policy = conf.policy; + int policy = conf->policy; if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) { oss_logerr2 (errno, typ, "Failed to set timing policy to %d\n", - conf.policy); + conf->policy); goto err; } setfragment = 0; @@ -458,19 +449,12 @@ static int oss_run_out (HWVoiceOut *hw, int live) } if (abinfo.bytes > bufsize) { - if (conf.debug) { - dolog ("warning: Invalid available size, size=%d bufsize=%d\n" - "please report your OS/audio hw to av1474@comtv.ru\n", - abinfo.bytes, bufsize); - } + trace_oss_invalid_available_size(abinfo.bytes, bufsize); abinfo.bytes = bufsize; } if (abinfo.bytes < 0) { - if (conf.debug) { - dolog ("warning: Invalid available size, size=%d bufsize=%d\n", - abinfo.bytes, bufsize); - } + trace_oss_invalid_available_size(abinfo.bytes, bufsize); return 0; } @@ -510,7 +494,8 @@ static void oss_fini_out (HWVoiceOut *hw) } } -static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) +static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) { OSSVoiceOut *oss = (OSSVoiceOut *) hw; struct oss_params req, obt; @@ -519,16 +504,17 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) int fd; audfmt_e effective_fmt; struct audsettings obt_as; + OSSConf *conf = drv_opaque; oss->fd = -1; req.fmt = aud_to_ossfmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; - req.fragsize = conf.fragsize; - req.nfrags = conf.nfrags; + req.fragsize = conf->fragsize; + req.nfrags = conf->nfrags; - if (oss_open (0, &req, &obt, &fd)) { + if (oss_open (0, &req, &obt, &fd, conf)) { return -1; } @@ -555,7 +541,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; oss->mmapped = 0; - if (conf.try_mmap) { + if (conf->try_mmap) { oss->pcm_buf = mmap ( NULL, hw->samples << hw->info.shift, @@ -615,6 +601,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) } oss->fd = fd; + oss->conf = conf; return 0; } @@ -634,7 +621,8 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) va_end (ap); ldebug ("enabling voice\n"); - if (poll_mode && oss_poll_out (hw)) { + if (poll_mode) { + oss_poll_out (hw); poll_mode = 0; } hw->poll_mode = poll_mode; @@ -676,7 +664,7 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) return 0; } -static int oss_init_in (HWVoiceIn *hw, struct audsettings *as) +static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) { OSSVoiceIn *oss = (OSSVoiceIn *) hw; struct oss_params req, obt; @@ -685,15 +673,16 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as) int fd; audfmt_e effective_fmt; struct audsettings obt_as; + OSSConf *conf = drv_opaque; oss->fd = -1; req.fmt = aud_to_ossfmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; - req.fragsize = conf.fragsize; - req.nfrags = conf.nfrags; - if (oss_open (1, &req, &obt, &fd)) { + req.fragsize = conf->fragsize; + req.nfrags = conf->nfrags; + if (oss_open (1, &req, &obt, &fd, conf)) { return -1; } @@ -727,6 +716,7 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as) } oss->fd = fd; + oss->conf = conf; return 0; } @@ -828,7 +818,8 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) poll_mode = va_arg (ap, int); va_end (ap); - if (poll_mode && oss_poll_in (hw)) { + if (poll_mode) { + oss_poll_in (hw); poll_mode = 0; } hw->poll_mode = poll_mode; @@ -845,71 +836,78 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) return 0; } +static OSSConf glob_conf = { + .try_mmap = 0, + .nfrags = 4, + .fragsize = 4096, + .devpath_out = "/dev/dsp", + .devpath_in = "/dev/dsp", + .exclusive = 0, + .policy = 5 +}; + static void *oss_audio_init (void) { - if (access(conf.devpath_in, R_OK | W_OK) < 0 || - access(conf.devpath_out, R_OK | W_OK) < 0) { + OSSConf *conf = g_malloc(sizeof(OSSConf)); + *conf = glob_conf; + + if (access(conf->devpath_in, R_OK | W_OK) < 0 || + access(conf->devpath_out, R_OK | W_OK) < 0) { return NULL; } - return &conf; + return conf; } static void oss_audio_fini (void *opaque) { - (void) opaque; + g_free(opaque); } static struct audio_option oss_options[] = { { .name = "FRAGSIZE", .tag = AUD_OPT_INT, - .valp = &conf.fragsize, + .valp = &glob_conf.fragsize, .descr = "Fragment size in bytes" }, { .name = "NFRAGS", .tag = AUD_OPT_INT, - .valp = &conf.nfrags, + .valp = &glob_conf.nfrags, .descr = "Number of fragments" }, { .name = "MMAP", .tag = AUD_OPT_BOOL, - .valp = &conf.try_mmap, + .valp = &glob_conf.try_mmap, .descr = "Try using memory mapped access" }, { .name = "DAC_DEV", .tag = AUD_OPT_STR, - .valp = &conf.devpath_out, + .valp = &glob_conf.devpath_out, .descr = "Path to DAC device" }, { .name = "ADC_DEV", .tag = AUD_OPT_STR, - .valp = &conf.devpath_in, + .valp = &glob_conf.devpath_in, .descr = "Path to ADC device" }, { .name = "EXCLUSIVE", .tag = AUD_OPT_BOOL, - .valp = &conf.exclusive, + .valp = &glob_conf.exclusive, .descr = "Open device in exclusive mode (vmix wont work)" }, #ifdef USE_DSP_POLICY { .name = "POLICY", .tag = AUD_OPT_INT, - .valp = &conf.policy, + .valp = &glob_conf.policy, .descr = "Set the timing policy of the device, -1 to use fragment mode", }, #endif - { - .name = "DEBUG", - .tag = AUD_OPT_BOOL, - .valp = &conf.debug, - .descr = "Turn on some debugging messages" - }, { /* End of list */ } }; diff --git a/audio/paaudio.c b/audio/paaudio.c index 90ff245..fea6071 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -9,6 +9,19 @@ #include "audio_pt_int.h" typedef struct { + int samples; + char *server; + char *sink; + char *source; +} PAConf; + +typedef struct { + PAConf conf; + pa_threaded_mainloop *mainloop; + pa_context *context; +} paaudio; + +typedef struct { HWVoiceOut hw; int done; int live; @@ -17,6 +30,7 @@ typedef struct { pa_stream *stream; void *pcm_buf; struct audio_pt pt; + paaudio *g; } PAVoiceOut; typedef struct { @@ -30,20 +44,10 @@ typedef struct { struct audio_pt pt; const void *read_data; size_t read_index, read_length; + paaudio *g; } PAVoiceIn; -typedef struct { - int samples; - char *server; - char *sink; - char *source; - pa_threaded_mainloop *mainloop; - pa_context *context; -} paaudio; - -static paaudio glob_paaudio = { - .samples = 4096, -}; +static void qpa_audio_fini(void *opaque); static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) { @@ -106,7 +110,7 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) { - paaudio *g = &glob_paaudio; + paaudio *g = p->g; pa_threaded_mainloop_lock (g->mainloop); @@ -160,7 +164,7 @@ unlock_and_fail: static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) { - paaudio *g = &glob_paaudio; + paaudio *g = p->g; pa_threaded_mainloop_lock (g->mainloop); @@ -222,7 +226,7 @@ static void *qpa_thread_out (void *arg) } } - decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2); + decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2); rpos = pa->rpos; if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { @@ -314,7 +318,7 @@ static void *qpa_thread_in (void *arg) } } - incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2); + incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2); wpos = pa->wpos; if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { @@ -430,7 +434,7 @@ static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) static void context_state_cb (pa_context *c, void *userdata) { - paaudio *g = &glob_paaudio; + paaudio *g = userdata; switch (pa_context_get_state(c)) { case PA_CONTEXT_READY: @@ -449,7 +453,7 @@ static void context_state_cb (pa_context *c, void *userdata) static void stream_state_cb (pa_stream *s, void * userdata) { - paaudio *g = &glob_paaudio; + paaudio *g = userdata; switch (pa_stream_get_state (s)) { @@ -467,23 +471,21 @@ static void stream_state_cb (pa_stream *s, void * userdata) static void stream_request_cb (pa_stream *s, size_t length, void *userdata) { - paaudio *g = &glob_paaudio; + paaudio *g = userdata; pa_threaded_mainloop_signal (g->mainloop, 0); } static pa_stream *qpa_simple_new ( - const char *server, + paaudio *g, const char *name, pa_stream_direction_t dir, const char *dev, - const char *stream_name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_buffer_attr *attr, int *rerror) { - paaudio *g = &glob_paaudio; int r; pa_stream *stream; @@ -534,13 +536,15 @@ fail: return NULL; } -static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) +static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) { int error; - static pa_sample_spec ss; - static pa_buffer_attr ba; + pa_sample_spec ss; + pa_buffer_attr ba; struct audsettings obt_as = *as; PAVoiceOut *pa = (PAVoiceOut *) hw; + paaudio *g = pa->g = drv_opaque; ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.channels = as->nchannels; @@ -558,11 +562,10 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); pa->stream = qpa_simple_new ( - glob_paaudio.server, + g, "qemu", PA_STREAM_PLAYBACK, - glob_paaudio.sink, - "pcm.playback", + g->conf.sink, &ss, NULL, /* channel map */ &ba, /* buffering attributes */ @@ -574,7 +577,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) } audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = glob_paaudio.samples; + hw->samples = g->conf.samples; pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); pa->rpos = hw->rpos; if (!pa->pcm_buf) { @@ -601,12 +604,13 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as) return -1; } -static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) +static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) { int error; - static pa_sample_spec ss; + pa_sample_spec ss; struct audsettings obt_as = *as; PAVoiceIn *pa = (PAVoiceIn *) hw; + paaudio *g = pa->g = drv_opaque; ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.channels = as->nchannels; @@ -615,11 +619,10 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); pa->stream = qpa_simple_new ( - glob_paaudio.server, + g, "qemu", PA_STREAM_RECORD, - glob_paaudio.source, - "pcm.capture", + g->conf.source, &ss, NULL, /* channel map */ NULL, /* buffering attributes */ @@ -631,7 +634,7 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) } audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = glob_paaudio.samples; + hw->samples = g->conf.samples; pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); pa->wpos = hw->wpos; if (!pa->pcm_buf) { @@ -703,7 +706,7 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) PAVoiceOut *pa = (PAVoiceOut *) hw; pa_operation *op; pa_cvolume v; - paaudio *g = &glob_paaudio; + paaudio *g = pa->g; #ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */ pa_cvolume_init (&v); /* function is present in 0.9.13+ */ @@ -755,7 +758,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) PAVoiceIn *pa = (PAVoiceIn *) hw; pa_operation *op; pa_cvolume v; - paaudio *g = &glob_paaudio; + paaudio *g = pa->g; #ifdef PA_CHECK_VERSION pa_cvolume_init (&v); @@ -805,23 +808,31 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) } /* common */ +static PAConf glob_conf = { + .samples = 4096, +}; + static void *qpa_audio_init (void) { - paaudio *g = &glob_paaudio; + paaudio *g = g_malloc(sizeof(paaudio)); + g->conf = glob_conf; + g->mainloop = NULL; + g->context = NULL; g->mainloop = pa_threaded_mainloop_new (); if (!g->mainloop) { goto fail; } - g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server); + g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), + g->conf.server); if (!g->context) { goto fail; } pa_context_set_state_callback (g->context, context_state_cb, g); - if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) { + if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) { qpa_logerr (pa_context_errno (g->context), "pa_context_connect() failed\n"); goto fail; @@ -854,12 +865,13 @@ static void *qpa_audio_init (void) pa_threaded_mainloop_unlock (g->mainloop); - return &glob_paaudio; + return g; unlock_and_fail: pa_threaded_mainloop_unlock (g->mainloop); fail: AUD_log (AUDIO_CAP, "Failed to initialize PA context"); + qpa_audio_fini(g); return NULL; } @@ -874,39 +886,38 @@ static void qpa_audio_fini (void *opaque) if (g->context) { pa_context_disconnect (g->context); pa_context_unref (g->context); - g->context = NULL; } if (g->mainloop) { pa_threaded_mainloop_free (g->mainloop); } - g->mainloop = NULL; + g_free(g); } struct audio_option qpa_options[] = { { .name = "SAMPLES", .tag = AUD_OPT_INT, - .valp = &glob_paaudio.samples, + .valp = &glob_conf.samples, .descr = "buffer size in samples" }, { .name = "SERVER", .tag = AUD_OPT_STR, - .valp = &glob_paaudio.server, + .valp = &glob_conf.server, .descr = "server address" }, { .name = "SINK", .tag = AUD_OPT_STR, - .valp = &glob_paaudio.sink, + .valp = &glob_conf.sink, .descr = "sink device name" }, { .name = "SOURCE", .tag = AUD_OPT_STR, - .valp = &glob_paaudio.source, + .valp = &glob_conf.source, .descr = "source device name" }, { /* End of list */ } diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index d24daa5..1140f2e 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -55,6 +55,7 @@ static struct SDLAudioState { SDL_mutex *mutex; SDL_sem *sem; int initialized; + bool driver_created; } glob_sdl; typedef struct SDLAudioState SDLAudioState; @@ -332,7 +333,8 @@ static void sdl_fini_out (HWVoiceOut *hw) sdl_close (&glob_sdl); } -static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as) +static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) { SDLVoiceOut *sdl = (SDLVoiceOut *) hw; SDLAudioState *s = &glob_sdl; @@ -392,6 +394,10 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) static void *sdl_audio_init (void) { SDLAudioState *s = &glob_sdl; + if (s->driver_created) { + sdl_logerr("Can't create multiple sdl backends\n"); + return NULL; + } if (SDL_InitSubSystem (SDL_INIT_AUDIO)) { sdl_logerr ("SDL failed to initialize audio subsystem\n"); @@ -413,6 +419,7 @@ static void *sdl_audio_init (void) return NULL; } + s->driver_created = true; return s; } @@ -423,6 +430,7 @@ static void sdl_audio_fini (void *opaque) SDL_DestroySemaphore (s->sem); SDL_DestroyMutex (s->mutex); SDL_QuitSubSystem (SDL_INIT_AUDIO); + s->driver_created = false; } static struct audio_option sdl_options[] = { diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c index 7b79bed..5c6f726 100644 --- a/audio/spiceaudio.c +++ b/audio/spiceaudio.c @@ -115,7 +115,8 @@ static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate) /* playback */ -static int line_out_init (HWVoiceOut *hw, struct audsettings *as) +static int line_out_init(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) { SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); struct audsettings settings; @@ -243,7 +244,7 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...) /* record */ -static int line_in_init (HWVoiceIn *hw, struct audsettings *as) +static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) { SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); struct audsettings settings; diff --git a/audio/wavaudio.c b/audio/wavaudio.c index 6846a1a..c586020 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -36,15 +36,10 @@ typedef struct WAVVoiceOut { int total_samples; } WAVVoiceOut; -static struct { +typedef struct { struct audsettings settings; const char *wav_path; -} conf = { - .settings.freq = 44100, - .settings.nchannels = 2, - .settings.fmt = AUD_FMT_S16, - .wav_path = "qemu.wav" -}; +} WAVConf; static int wav_run_out (HWVoiceOut *hw, int live) { @@ -105,7 +100,8 @@ static void le_store (uint8_t *buf, uint32_t val, int len) } } -static int wav_init_out (HWVoiceOut *hw, struct audsettings *as) +static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, + void *drv_opaque) { WAVVoiceOut *wav = (WAVVoiceOut *) hw; int bits16 = 0, stereo = 0; @@ -115,9 +111,8 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as) 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 }; - struct audsettings wav_as = conf.settings; - - (void) as; + WAVConf *conf = drv_opaque; + struct audsettings wav_as = conf->settings; stereo = wav_as.nchannels == 2; switch (wav_as.fmt) { @@ -155,10 +150,10 @@ static int wav_init_out (HWVoiceOut *hw, struct audsettings *as) le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); le_store (hdr + 32, 1 << (bits16 + stereo), 2); - wav->f = fopen (conf.wav_path, "wb"); + wav->f = fopen (conf->wav_path, "wb"); if (!wav->f) { dolog ("Failed to open wave file `%s'\nReason: %s\n", - conf.wav_path, strerror (errno)); + conf->wav_path, strerror (errno)); g_free (wav->pcm_buf); wav->pcm_buf = NULL; return -1; @@ -226,40 +221,49 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) return 0; } +static WAVConf glob_conf = { + .settings.freq = 44100, + .settings.nchannels = 2, + .settings.fmt = AUD_FMT_S16, + .wav_path = "qemu.wav" +}; + static void *wav_audio_init (void) { - return &conf; + WAVConf *conf = g_malloc(sizeof(WAVConf)); + *conf = glob_conf; + return conf; } static void wav_audio_fini (void *opaque) { - (void) opaque; ldebug ("wav_fini"); + g_free(opaque); } static struct audio_option wav_options[] = { { .name = "FREQUENCY", .tag = AUD_OPT_INT, - .valp = &conf.settings.freq, + .valp = &glob_conf.settings.freq, .descr = "Frequency" }, { .name = "FORMAT", .tag = AUD_OPT_FMT, - .valp = &conf.settings.fmt, + .valp = &glob_conf.settings.fmt, .descr = "Format" }, { .name = "DAC_FIXED_CHANNELS", .tag = AUD_OPT_INT, - .valp = &conf.settings.nchannels, + .valp = &glob_conf.settings.nchannels, .descr = "Number of channels (1 - mono, 2 - stereo)" }, { .name = "PATH", .tag = AUD_OPT_STR, - .valp = &conf.wav_path, + .valp = &glob_conf.wav_path, .descr = "Path to wave file" }, { /* End of list */ } diff --git a/audio/winwaveaudio.c b/audio/winwaveaudio.c deleted file mode 100644 index 8dbd145..0000000 --- a/audio/winwaveaudio.c +++ /dev/null @@ -1,717 +0,0 @@ -/* public domain */ - -#include "qemu-common.h" -#include "sysemu/sysemu.h" -#include "audio.h" - -#define AUDIO_CAP "winwave" -#include "audio_int.h" - -#include <windows.h> -#include <mmsystem.h> - -#include "audio_win_int.h" - -static struct { - int dac_headers; - int dac_samples; - int adc_headers; - int adc_samples; -} conf = { - .dac_headers = 4, - .dac_samples = 1024, - .adc_headers = 4, - .adc_samples = 1024 -}; - -typedef struct { - HWVoiceOut hw; - HWAVEOUT hwo; - WAVEHDR *hdrs; - HANDLE event; - void *pcm_buf; - int avail; - int pending; - int curhdr; - int paused; - CRITICAL_SECTION crit_sect; -} WaveVoiceOut; - -typedef struct { - HWVoiceIn hw; - HWAVEIN hwi; - WAVEHDR *hdrs; - HANDLE event; - void *pcm_buf; - int curhdr; - int paused; - int rpos; - int avail; - CRITICAL_SECTION crit_sect; -} WaveVoiceIn; - -static void winwave_log_mmresult (MMRESULT mr) -{ - const char *str = "BUG"; - - switch (mr) { - case MMSYSERR_NOERROR: - str = "Success"; - break; - - case MMSYSERR_INVALHANDLE: - str = "Specified device handle is invalid"; - break; - - case MMSYSERR_BADDEVICEID: - str = "Specified device id is out of range"; - break; - - case MMSYSERR_NODRIVER: - str = "No device driver is present"; - break; - - case MMSYSERR_NOMEM: - str = "Unable to allocate or lock memory"; - break; - - case WAVERR_SYNC: - str = "Device is synchronous but waveOutOpen was called " - "without using the WINWAVE_ALLOWSYNC flag"; - break; - - case WAVERR_UNPREPARED: - str = "The data block pointed to by the pwh parameter " - "hasn't been prepared"; - break; - - case WAVERR_STILLPLAYING: - str = "There are still buffers in the queue"; - break; - - default: - dolog ("Reason: Unknown (MMRESULT %#x)\n", mr); - return; - } - - dolog ("Reason: %s\n", str); -} - -static void GCC_FMT_ATTR (2, 3) winwave_logerr ( - MMRESULT mr, - const char *fmt, - ... - ) -{ - va_list ap; - - va_start (ap, fmt); - AUD_vlog (AUDIO_CAP, fmt, ap); - va_end (ap); - - AUD_log (NULL, " failed\n"); - winwave_log_mmresult (mr); -} - -static void winwave_anal_close_out (WaveVoiceOut *wave) -{ - MMRESULT mr; - - mr = waveOutClose (wave->hwo); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutClose"); - } - wave->hwo = NULL; -} - -static void CALLBACK winwave_callback_out ( - HWAVEOUT hwo, - UINT msg, - DWORD_PTR dwInstance, - DWORD_PTR dwParam1, - DWORD_PTR dwParam2 - ) -{ - WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance; - - switch (msg) { - case WOM_DONE: - { - WAVEHDR *h = (WAVEHDR *) dwParam1; - if (!h->dwUser) { - h->dwUser = 1; - EnterCriticalSection (&wave->crit_sect); - { - wave->avail += conf.dac_samples; - } - LeaveCriticalSection (&wave->crit_sect); - if (wave->hw.poll_mode) { - if (!SetEvent (wave->event)) { - dolog ("DAC SetEvent failed %lx\n", GetLastError ()); - } - } - } - } - break; - - case WOM_CLOSE: - case WOM_OPEN: - break; - - default: - dolog ("unknown wave out callback msg %x\n", msg); - } -} - -static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as) -{ - int i; - int err; - MMRESULT mr; - WAVEFORMATEX wfx; - WaveVoiceOut *wave; - - wave = (WaveVoiceOut *) hw; - - InitializeCriticalSection (&wave->crit_sect); - - err = waveformat_from_audio_settings (&wfx, as); - if (err) { - goto err0; - } - - mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx, - (DWORD_PTR) winwave_callback_out, - (DWORD_PTR) wave, CALLBACK_FUNCTION); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutOpen"); - goto err1; - } - - wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers, - sizeof (*wave->hdrs)); - if (!wave->hdrs) { - goto err2; - } - - audio_pcm_init_info (&hw->info, as); - hw->samples = conf.dac_samples * conf.dac_headers; - wave->avail = hw->samples; - - wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples, - conf.dac_headers << hw->info.shift); - if (!wave->pcm_buf) { - goto err3; - } - - for (i = 0; i < conf.dac_headers; ++i) { - WAVEHDR *h = &wave->hdrs[i]; - - h->dwUser = 0; - h->dwBufferLength = conf.dac_samples << hw->info.shift; - h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength); - h->dwFlags = 0; - - mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h)); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutPrepareHeader(%d)", i); - goto err4; - } - } - - return 0; - - err4: - g_free (wave->pcm_buf); - err3: - g_free (wave->hdrs); - err2: - winwave_anal_close_out (wave); - err1: - err0: - return -1; -} - -static int winwave_write (SWVoiceOut *sw, void *buf, int len) -{ - return audio_pcm_sw_write (sw, buf, len); -} - -static int winwave_run_out (HWVoiceOut *hw, int live) -{ - WaveVoiceOut *wave = (WaveVoiceOut *) hw; - int decr; - int doreset; - - EnterCriticalSection (&wave->crit_sect); - { - decr = audio_MIN (live, wave->avail); - decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending); - wave->pending += decr; - wave->avail -= decr; - } - LeaveCriticalSection (&wave->crit_sect); - - doreset = hw->poll_mode && (wave->pending >= conf.dac_samples); - if (doreset && !ResetEvent (wave->event)) { - dolog ("DAC ResetEvent failed %lx\n", GetLastError ()); - } - - while (wave->pending >= conf.dac_samples) { - MMRESULT mr; - WAVEHDR *h = &wave->hdrs[wave->curhdr]; - - h->dwUser = 0; - mr = waveOutWrite (wave->hwo, h, sizeof (*h)); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr); - break; - } - - wave->pending -= conf.dac_samples; - wave->curhdr = (wave->curhdr + 1) % conf.dac_headers; - } - - return decr; -} - -static void winwave_poll (void *opaque) -{ - (void) opaque; - audio_run ("winwave_poll"); -} - -static void winwave_fini_out (HWVoiceOut *hw) -{ - int i; - MMRESULT mr; - WaveVoiceOut *wave = (WaveVoiceOut *) hw; - - mr = waveOutReset (wave->hwo); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutReset"); - } - - for (i = 0; i < conf.dac_headers; ++i) { - mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i], - sizeof (wave->hdrs[i])); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i); - } - } - - winwave_anal_close_out (wave); - - if (wave->event) { - qemu_del_wait_object (wave->event, winwave_poll, wave); - if (!CloseHandle (wave->event)) { - dolog ("DAC CloseHandle failed %lx\n", GetLastError ()); - } - wave->event = NULL; - } - - g_free (wave->pcm_buf); - wave->pcm_buf = NULL; - - g_free (wave->hdrs); - wave->hdrs = NULL; -} - -static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...) -{ - MMRESULT mr; - WaveVoiceOut *wave = (WaveVoiceOut *) hw; - - switch (cmd) { - case VOICE_ENABLE: - { - va_list ap; - int poll_mode; - - va_start (ap, cmd); - poll_mode = va_arg (ap, int); - va_end (ap); - - if (poll_mode && !wave->event) { - wave->event = CreateEvent (NULL, TRUE, TRUE, NULL); - if (!wave->event) { - dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n", - GetLastError ()); - } - } - - if (wave->event) { - int ret; - - ret = qemu_add_wait_object (wave->event, winwave_poll, wave); - hw->poll_mode = (ret == 0); - } - else { - hw->poll_mode = 0; - } - wave->paused = 0; - } - return 0; - - case VOICE_DISABLE: - if (!wave->paused) { - mr = waveOutReset (wave->hwo); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutReset"); - } - else { - wave->paused = 1; - } - } - if (wave->event) { - qemu_del_wait_object (wave->event, winwave_poll, wave); - } - return 0; - } - return -1; -} - -static void winwave_anal_close_in (WaveVoiceIn *wave) -{ - MMRESULT mr; - - mr = waveInClose (wave->hwi); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInClose"); - } - wave->hwi = NULL; -} - -static void CALLBACK winwave_callback_in ( - HWAVEIN *hwi, - UINT msg, - DWORD_PTR dwInstance, - DWORD_PTR dwParam1, - DWORD_PTR dwParam2 - ) -{ - WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance; - - switch (msg) { - case WIM_DATA: - { - WAVEHDR *h = (WAVEHDR *) dwParam1; - if (!h->dwUser) { - h->dwUser = 1; - EnterCriticalSection (&wave->crit_sect); - { - wave->avail += conf.adc_samples; - } - LeaveCriticalSection (&wave->crit_sect); - if (wave->hw.poll_mode) { - if (!SetEvent (wave->event)) { - dolog ("ADC SetEvent failed %lx\n", GetLastError ()); - } - } - } - } - break; - - case WIM_CLOSE: - case WIM_OPEN: - break; - - default: - dolog ("unknown wave in callback msg %x\n", msg); - } -} - -static void winwave_add_buffers (WaveVoiceIn *wave, int samples) -{ - int doreset; - - doreset = wave->hw.poll_mode && (samples >= conf.adc_samples); - if (doreset && !ResetEvent (wave->event)) { - dolog ("ADC ResetEvent failed %lx\n", GetLastError ()); - } - - while (samples >= conf.adc_samples) { - MMRESULT mr; - WAVEHDR *h = &wave->hdrs[wave->curhdr]; - - h->dwUser = 0; - mr = waveInAddBuffer (wave->hwi, h, sizeof (*h)); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr); - } - wave->curhdr = (wave->curhdr + 1) % conf.adc_headers; - samples -= conf.adc_samples; - } -} - -static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as) -{ - int i; - int err; - MMRESULT mr; - WAVEFORMATEX wfx; - WaveVoiceIn *wave; - - wave = (WaveVoiceIn *) hw; - - InitializeCriticalSection (&wave->crit_sect); - - err = waveformat_from_audio_settings (&wfx, as); - if (err) { - goto err0; - } - - mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx, - (DWORD_PTR) winwave_callback_in, - (DWORD_PTR) wave, CALLBACK_FUNCTION); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInOpen"); - goto err1; - } - - wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers, - sizeof (*wave->hdrs)); - if (!wave->hdrs) { - goto err2; - } - - audio_pcm_init_info (&hw->info, as); - hw->samples = conf.adc_samples * conf.adc_headers; - wave->avail = 0; - - wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples, - conf.adc_headers << hw->info.shift); - if (!wave->pcm_buf) { - goto err3; - } - - for (i = 0; i < conf.adc_headers; ++i) { - WAVEHDR *h = &wave->hdrs[i]; - - h->dwUser = 0; - h->dwBufferLength = conf.adc_samples << hw->info.shift; - h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength); - h->dwFlags = 0; - - mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h)); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInPrepareHeader(%d)", i); - goto err4; - } - } - - wave->paused = 1; - winwave_add_buffers (wave, hw->samples); - return 0; - - err4: - g_free (wave->pcm_buf); - err3: - g_free (wave->hdrs); - err2: - winwave_anal_close_in (wave); - err1: - err0: - return -1; -} - -static void winwave_fini_in (HWVoiceIn *hw) -{ - int i; - MMRESULT mr; - WaveVoiceIn *wave = (WaveVoiceIn *) hw; - - mr = waveInReset (wave->hwi); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInReset"); - } - - for (i = 0; i < conf.adc_headers; ++i) { - mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i], - sizeof (wave->hdrs[i])); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInUnprepareHeader(%d)", i); - } - } - - winwave_anal_close_in (wave); - - if (wave->event) { - qemu_del_wait_object (wave->event, winwave_poll, wave); - if (!CloseHandle (wave->event)) { - dolog ("ADC CloseHandle failed %lx\n", GetLastError ()); - } - wave->event = NULL; - } - - g_free (wave->pcm_buf); - wave->pcm_buf = NULL; - - g_free (wave->hdrs); - wave->hdrs = NULL; -} - -static int winwave_run_in (HWVoiceIn *hw) -{ - WaveVoiceIn *wave = (WaveVoiceIn *) hw; - int live = audio_pcm_hw_get_live_in (hw); - int dead = hw->samples - live; - int decr, ret; - - if (!dead) { - return 0; - } - - EnterCriticalSection (&wave->crit_sect); - { - decr = audio_MIN (dead, wave->avail); - wave->avail -= decr; - } - LeaveCriticalSection (&wave->crit_sect); - - ret = decr; - while (decr) { - int left = hw->samples - hw->wpos; - int conv = audio_MIN (left, decr); - hw->conv (hw->conv_buf + hw->wpos, - advance (wave->pcm_buf, wave->rpos << hw->info.shift), - conv); - - wave->rpos = (wave->rpos + conv) % hw->samples; - hw->wpos = (hw->wpos + conv) % hw->samples; - decr -= conv; - } - - winwave_add_buffers (wave, ret); - return ret; -} - -static int winwave_read (SWVoiceIn *sw, void *buf, int size) -{ - return audio_pcm_sw_read (sw, buf, size); -} - -static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...) -{ - MMRESULT mr; - WaveVoiceIn *wave = (WaveVoiceIn *) hw; - - switch (cmd) { - case VOICE_ENABLE: - { - va_list ap; - int poll_mode; - - va_start (ap, cmd); - poll_mode = va_arg (ap, int); - va_end (ap); - - if (poll_mode && !wave->event) { - wave->event = CreateEvent (NULL, TRUE, TRUE, NULL); - if (!wave->event) { - dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n", - GetLastError ()); - } - } - - if (wave->event) { - int ret; - - ret = qemu_add_wait_object (wave->event, winwave_poll, wave); - hw->poll_mode = (ret == 0); - } - else { - hw->poll_mode = 0; - } - if (wave->paused) { - mr = waveInStart (wave->hwi); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInStart"); - } - wave->paused = 0; - } - } - return 0; - - case VOICE_DISABLE: - if (!wave->paused) { - mr = waveInStop (wave->hwi); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInStop"); - } - else { - wave->paused = 1; - } - } - if (wave->event) { - qemu_del_wait_object (wave->event, winwave_poll, wave); - } - return 0; - } - return 0; -} - -static void *winwave_audio_init (void) -{ - return &conf; -} - -static void winwave_audio_fini (void *opaque) -{ - (void) opaque; -} - -static struct audio_option winwave_options[] = { - { - .name = "DAC_HEADERS", - .tag = AUD_OPT_INT, - .valp = &conf.dac_headers, - .descr = "DAC number of headers", - }, - { - .name = "DAC_SAMPLES", - .tag = AUD_OPT_INT, - .valp = &conf.dac_samples, - .descr = "DAC number of samples per header", - }, - { - .name = "ADC_HEADERS", - .tag = AUD_OPT_INT, - .valp = &conf.adc_headers, - .descr = "ADC number of headers", - }, - { - .name = "ADC_SAMPLES", - .tag = AUD_OPT_INT, - .valp = &conf.adc_samples, - .descr = "ADC number of samples per header", - }, - { /* End of list */ } -}; - -static struct audio_pcm_ops winwave_pcm_ops = { - .init_out = winwave_init_out, - .fini_out = winwave_fini_out, - .run_out = winwave_run_out, - .write = winwave_write, - .ctl_out = winwave_ctl_out, - .init_in = winwave_init_in, - .fini_in = winwave_fini_in, - .run_in = winwave_run_in, - .read = winwave_read, - .ctl_in = winwave_ctl_in -}; - -struct audio_driver winwave_audio_driver = { - .name = "winwave", - .descr = "Windows Waveform Audio http://msdn.microsoft.com", - .options = winwave_options, - .init = winwave_audio_init, - .fini = winwave_audio_fini, - .pcm_ops = &winwave_pcm_ops, - .can_be_default = 1, - .max_voices_out = INT_MAX, - .max_voices_in = INT_MAX, - .voice_size_out = sizeof (WaveVoiceOut), - .voice_size_in = sizeof (WaveVoiceIn) -}; @@ -36,6 +36,7 @@ #include "qmp-commands.h" #include "qemu/timer.h" #include "qapi-event.h" +#include "block/throttle-groups.h" #ifdef CONFIG_BSD #include <sys/types.h> @@ -79,6 +80,12 @@ static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = static QLIST_HEAD(, BlockDriver) bdrv_drivers = QLIST_HEAD_INITIALIZER(bdrv_drivers); +static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, + const char *reference, QDict *options, int flags, + BlockDriverState *parent, + const BdrvChildRole *child_role, + BlockDriver *drv, Error **errp); + static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs); /* If non-zero, use only whitelisted block drivers */ static int use_bdrv_whitelist; @@ -682,8 +689,8 @@ static int bdrv_temp_snapshot_flags(int flags) } /* - * Returns the flags that bs->file should get, based on the given flags for - * the parent BDS + * Returns the flags that bs->file should get if a protocol driver is expected, + * based on the given flags for the parent BDS */ static int bdrv_inherited_flags(int flags) { @@ -700,6 +707,25 @@ static int bdrv_inherited_flags(int flags) return flags; } +const BdrvChildRole child_file = { + .inherit_flags = bdrv_inherited_flags, +}; + +/* + * Returns the flags that bs->file should get if the use of formats (and not + * only protocols) is permitted for it, based on the given flags for the parent + * BDS + */ +static int bdrv_inherited_fmt_flags(int parent_flags) +{ + int flags = child_file.inherit_flags(parent_flags); + return flags & ~BDRV_O_PROTOCOL; +} + +const BdrvChildRole child_format = { + .inherit_flags = bdrv_inherited_fmt_flags, +}; + /* * Returns the flags that bs->backing_hd should get, based on the given flags * for the parent BDS @@ -715,6 +741,10 @@ static int bdrv_backing_flags(int flags) return flags; } +static const BdrvChildRole child_backing = { + .inherit_flags = bdrv_backing_flags, +}; + static int bdrv_open_flags(BlockDriverState *bs, int flags) { int open_flags = flags | BDRV_O_CACHE_WB; @@ -767,6 +797,19 @@ static void bdrv_assign_node_name(BlockDriverState *bs, QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list); } +static QemuOptsList bdrv_runtime_opts = { + .name = "bdrv_common", + .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head), + .desc = { + { + .name = "node-name", + .type = QEMU_OPT_STRING, + .help = "Node name of the block device node", + }, + { /* end of list */ } + }, +}; + /* * Common part for opening disk images and files * @@ -778,6 +821,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, int ret, open_flags; const char *filename; const char *node_name = NULL; + QemuOpts *opts; Error *local_err = NULL; assert(drv != NULL); @@ -798,23 +842,22 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name); - node_name = qdict_get_try_str(options, "node-name"); - bdrv_assign_node_name(bs, node_name, &local_err); + opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { error_propagate(errp, local_err); - return -EINVAL; + ret = -EINVAL; + goto fail_opts; } - qdict_del(options, "node-name"); - /* bdrv_open() with directly using a protocol as drv. This layer is already - * opened, so assign it to bs (while file becomes a closed BlockDriverState) - * and return immediately. */ - if (file != NULL && drv->bdrv_file_open) { - bdrv_swap(file, bs); - return 0; + node_name = qemu_opt_get(opts, "node-name"); + bdrv_assign_node_name(bs, node_name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail_opts; } - bs->open_flags = flags; bs->guest_block_size = 512; bs->request_alignment = 512; bs->zero_beyond_eof = true; @@ -827,7 +870,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, ? "Driver '%s' can only be used for read-only devices" : "Driver '%s' is not whitelisted", drv->format_name); - return -ENOTSUP; + ret = -ENOTSUP; + goto fail_opts; } assert(bs->copy_on_read == 0); /* bdrv_new() and bdrv_close() make it so */ @@ -836,7 +880,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, bdrv_enable_copy_on_read(bs); } else { error_setg(errp, "Can't use copy-on-read on read-only device"); - return -EINVAL; + ret = -EINVAL; + goto fail_opts; } } @@ -902,6 +947,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, assert(bdrv_opt_mem_align(bs) != 0); assert(bdrv_min_mem_align(bs) != 0); assert((bs->request_alignment != 0) || bs->sg); + + qemu_opts_del(opts); return 0; free_and_fail: @@ -909,6 +956,8 @@ free_and_fail: g_free(bs->opaque); bs->opaque = NULL; bs->drv = NULL; +fail_opts: + qemu_opts_del(opts); return ret; } @@ -942,14 +991,17 @@ static QDict *parse_json_filename(const char *filename, Error **errp) /* * Fills in default options for opening images and converts the legacy * filename/flags pair to option QDict entries. + * The BDRV_O_PROTOCOL flag in *flags will be set or cleared accordingly if a + * block driver has been specified explicitly. */ -static int bdrv_fill_options(QDict **options, const char **pfilename, int flags, - BlockDriver *drv, Error **errp) +static int bdrv_fill_options(QDict **options, const char **pfilename, + int *flags, BlockDriver *drv, Error **errp) { const char *filename = *pfilename; const char *drvname; - bool protocol = flags & BDRV_O_PROTOCOL; + bool protocol = *flags & BDRV_O_PROTOCOL; bool parse_filename = false; + BlockDriver *tmp_drv; Error *local_err = NULL; /* Parse json: pseudo-protocol */ @@ -967,6 +1019,24 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags, *pfilename = filename = NULL; } + drvname = qdict_get_try_str(*options, "driver"); + + /* If the user has explicitly specified the driver, this choice should + * override the BDRV_O_PROTOCOL flag */ + tmp_drv = drv; + if (!tmp_drv && drvname) { + tmp_drv = bdrv_find_format(drvname); + } + if (tmp_drv) { + protocol = tmp_drv->bdrv_file_open; + } + + if (protocol) { + *flags |= BDRV_O_PROTOCOL; + } else { + *flags &= ~BDRV_O_PROTOCOL; + } + /* Fetch the file name from the options QDict if necessary */ if (protocol && filename) { if (!qdict_haskey(*options, "filename")) { @@ -981,7 +1051,6 @@ static int bdrv_fill_options(QDict **options, const char **pfilename, int flags, /* Find the right block driver */ filename = qdict_get_try_str(*options, "filename"); - drvname = qdict_get_try_str(*options, "driver"); if (drv) { if (drvname) { @@ -1118,9 +1187,10 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) } assert(bs->backing_hd == NULL); - ret = bdrv_open(&backing_hd, - *backing_filename ? backing_filename : NULL, NULL, options, - bdrv_backing_flags(bs->open_flags), NULL, &local_err); + ret = bdrv_open_inherit(&backing_hd, + *backing_filename ? backing_filename : NULL, + NULL, options, 0, bs, &child_backing, + NULL, &local_err); if (ret < 0) { bdrv_unref(backing_hd); backing_hd = NULL; @@ -1154,7 +1224,8 @@ free_exit: * To conform with the behavior of bdrv_open(), *pbs has to be NULL. */ int bdrv_open_image(BlockDriverState **pbs, const char *filename, - QDict *options, const char *bdref_key, int flags, + QDict *options, const char *bdref_key, + BlockDriverState* parent, const BdrvChildRole *child_role, bool allow_none, Error **errp) { QDict *image_options; @@ -1182,7 +1253,8 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename, goto done; } - ret = bdrv_open(pbs, filename, reference, image_options, flags, NULL, errp); + ret = bdrv_open_inherit(pbs, filename, reference, image_options, 0, + parent, child_role, NULL, errp); done: qdict_del(options, bdref_key); @@ -1254,6 +1326,19 @@ out: return ret; } +static void bdrv_attach_child(BlockDriverState *parent_bs, + BlockDriverState *child_bs, + const BdrvChildRole *child_role) +{ + BdrvChild *child = g_new(BdrvChild, 1); + *child = (BdrvChild) { + .bs = child_bs, + .role = child_role, + }; + + QLIST_INSERT_HEAD(&parent_bs->children, child, next); +} + /* * Opens a disk image (raw, qcow2, vmdk, ...) * @@ -1269,9 +1354,11 @@ out: * should be opened. If specified, neither options nor a filename may be given, * nor can an existing BDS be reused (that is, *pbs has to be NULL). */ -int bdrv_open(BlockDriverState **pbs, const char *filename, - const char *reference, QDict *options, int flags, - BlockDriver *drv, Error **errp) +static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, + const char *reference, QDict *options, int flags, + BlockDriverState *parent, + const BdrvChildRole *child_role, + BlockDriver *drv, Error **errp) { int ret; BlockDriverState *file = NULL, *bs; @@ -1280,6 +1367,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, int snapshot_flags = 0; assert(pbs); + assert(!child_role || !flags); + assert(!child_role == !parent); if (reference) { bool options_non_empty = options ? qdict_size(options) : false; @@ -1302,6 +1391,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, return -ENODEV; } bdrv_ref(bs); + if (child_role) { + bdrv_attach_child(parent, bs, child_role); + } *pbs = bs; return 0; } @@ -1317,7 +1409,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, options = qdict_new(); } - ret = bdrv_fill_options(&options, &filename, flags, drv, &local_err); + if (child_role) { + bs->inherits_from = parent; + flags = child_role->inherit_flags(parent->open_flags); + } + + ret = bdrv_fill_options(&options, &filename, &flags, drv, &local_err); if (local_err) { goto fail; } @@ -1336,12 +1433,8 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, } assert(drvname || !(flags & BDRV_O_PROTOCOL)); - if (drv && !drv->bdrv_file_open) { - /* If the user explicitly wants a format driver here, we'll need to add - * another layer for the protocol in bs->file */ - flags &= ~BDRV_O_PROTOCOL; - } + bs->open_flags = flags; bs->options = options; options = qdict_clone_shallow(options); @@ -1356,9 +1449,9 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, } assert(file == NULL); + bs->open_flags = flags; ret = bdrv_open_image(&file, filename, options, "file", - bdrv_inherited_flags(flags), - true, &local_err); + bs, &child_file, true, &local_err); if (ret < 0) { goto fail; } @@ -1377,6 +1470,12 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, goto fail; } + /* BDRV_O_PROTOCOL must be set iff a protocol BDS is about to be created */ + assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->bdrv_file_open); + /* file must be NULL if a protocol BDS is about to be created + * (the inverse results in an error message from bdrv_open_common()) */ + assert(!(flags & BDRV_O_PROTOCOL) || !file); + /* Open the image */ ret = bdrv_open_common(bs, file, options, flags, drv, &local_err); if (ret < 0) { @@ -1439,6 +1538,10 @@ int bdrv_open(BlockDriverState **pbs, const char *filename, goto close_and_fail; } + if (child_role) { + bdrv_attach_child(parent, bs, child_role); + } + QDECREF(options); *pbs = bs; return 0; @@ -1475,6 +1578,14 @@ close_and_fail: return ret; } +int bdrv_open(BlockDriverState **pbs, const char *filename, + const char *reference, QDict *options, int flags, + BlockDriver *drv, Error **errp) +{ + return bdrv_open_inherit(pbs, filename, reference, options, flags, NULL, + NULL, drv, errp); +} + typedef struct BlockReopenQueueEntry { bool prepared; BDRVReopenState state; @@ -1505,6 +1616,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, assert(bs != NULL); BlockReopenQueueEntry *bs_entry; + BdrvChild *child; + if (bs_queue == NULL) { bs_queue = g_new0(BlockReopenQueue, 1); QSIMPLEQ_INIT(bs_queue); @@ -1513,8 +1626,15 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, /* bdrv_open() masks this flag out */ flags &= ~BDRV_O_PROTOCOL; - if (bs->file) { - bdrv_reopen_queue(bs_queue, bs->file, bdrv_inherited_flags(flags)); + QLIST_FOREACH(child, &bs->children, next) { + int child_flags; + + if (child->bs->inherits_from != bs) { + continue; + } + + child_flags = child->role->inherit_flags(flags); + bdrv_reopen_queue(bs_queue, child->bs, child_flags); } bs_entry = g_new0(BlockReopenQueueEntry, 1); @@ -1725,6 +1845,16 @@ void bdrv_close(BlockDriverState *bs) notifier_list_notify(&bs->close_notifiers, bs); if (bs->drv) { + BdrvChild *child, *next; + + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { + if (child->bs->inherits_from == bs) { + child->bs->inherits_from = NULL; + } + QLIST_REMOVE(child, next); + g_free(child); + } + if (bs->backing_hd) { BlockDriverState *backing_hd = bs->backing_hd; bdrv_set_backing_hd(bs, NULL); @@ -1822,12 +1952,18 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, bs_dest->enable_write_cache = bs_src->enable_write_cache; /* i/o throttled req */ - memcpy(&bs_dest->throttle_state, - &bs_src->throttle_state, - sizeof(ThrottleState)); + bs_dest->throttle_state = bs_src->throttle_state, + bs_dest->io_limits_enabled = bs_src->io_limits_enabled; + bs_dest->pending_reqs[0] = bs_src->pending_reqs[0]; + bs_dest->pending_reqs[1] = bs_src->pending_reqs[1]; bs_dest->throttled_reqs[0] = bs_src->throttled_reqs[0]; bs_dest->throttled_reqs[1] = bs_src->throttled_reqs[1]; - bs_dest->io_limits_enabled = bs_src->io_limits_enabled; + memcpy(&bs_dest->round_robin, + &bs_src->round_robin, + sizeof(bs_dest->round_robin)); + memcpy(&bs_dest->throttle_timers, + &bs_src->throttle_timers, + sizeof(ThrottleTimers)); /* r/w error */ bs_dest->on_read_error = bs_src->on_read_error; @@ -1869,6 +2005,10 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) { BlockDriverState tmp; + BdrvChild *child; + + bdrv_drain(bs_new); + bdrv_drain(bs_old); /* The code needs to swap the node_name but simply swapping node_list won't * work so first remove the nodes from the graph list, do the swap then @@ -1881,12 +2021,21 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) QTAILQ_REMOVE(&graph_bdrv_states, bs_old, node_list); } + /* If the BlockDriverState is part of a throttling group acquire + * its lock since we're going to mess with the protected fields. + * Otherwise there's no need to worry since no one else can touch + * them. */ + if (bs_old->throttle_state) { + throttle_group_lock(bs_old); + } + /* bs_new must be unattached and shouldn't have anything fancy enabled */ assert(!bs_new->blk); assert(QLIST_EMPTY(&bs_new->dirty_bitmaps)); assert(bs_new->job == NULL); assert(bs_new->io_limits_enabled == false); - assert(!throttle_have_timer(&bs_new->throttle_state)); + assert(bs_new->throttle_state == NULL); + assert(!throttle_timers_are_initialized(&bs_new->throttle_timers)); tmp = *bs_new; *bs_new = *bs_old; @@ -1903,7 +2052,13 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) /* Check a few fields that should remain attached to the device */ assert(bs_new->job == NULL); assert(bs_new->io_limits_enabled == false); - assert(!throttle_have_timer(&bs_new->throttle_state)); + assert(bs_new->throttle_state == NULL); + assert(!throttle_timers_are_initialized(&bs_new->throttle_timers)); + + /* Release the ThrottleGroup lock */ + if (bs_old->throttle_state) { + throttle_group_unlock(bs_old); + } /* insert the nodes back into the graph node list if needed */ if (bs_new->node_name[0] != '\0') { @@ -1913,6 +2068,30 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs_old, node_list); } + /* + * Update lh_first.le_prev for non-empty lists. + * + * The head of the op blocker list doesn't change because it is moved back + * in bdrv_move_feature_fields(). + */ + assert(QLIST_EMPTY(&bs_old->tracked_requests)); + assert(QLIST_EMPTY(&bs_new->tracked_requests)); + + QLIST_FIX_HEAD_PTR(&bs_new->children, next); + QLIST_FIX_HEAD_PTR(&bs_old->children, next); + + /* Update references in bs->opaque and children */ + QLIST_FOREACH(child, &bs_old->children, next) { + if (child->bs->inherits_from == bs_new) { + child->bs->inherits_from = bs_old; + } + } + QLIST_FOREACH(child, &bs_new->children, next) { + if (child->bs->inherits_from == bs_old) { + child->bs->inherits_from = bs_new; + } + } + bdrv_rebind(bs_new); bdrv_rebind(bs_old); } @@ -1935,6 +2114,7 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top) /* The contents of 'tmp' will become bs_top, as we are * swapping bs_new and bs_top contents. */ bdrv_set_backing_hd(bs_top, bs_new); + bdrv_attach_child(bs_top, bs_new, &child_backing); } static void bdrv_delete(BlockDriverState *bs) @@ -3220,10 +3400,9 @@ static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs) uint64_t size = bdrv_nb_sectors(bs); QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { - if (bdrv_dirty_bitmap_frozen(bitmap)) { - continue; - } + assert(!bdrv_dirty_bitmap_frozen(bitmap)); hbitmap_truncate(bitmap->bitmap, size); + bitmap->size = size; } } @@ -3691,7 +3870,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs) } if (bs->io_limits_enabled) { - throttle_detach_aio_context(&bs->throttle_state); + throttle_timers_detach_aio_context(&bs->throttle_timers); } if (bs->drv->bdrv_detach_aio_context) { bs->drv->bdrv_detach_aio_context(bs); @@ -3727,7 +3906,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs, bs->drv->bdrv_attach_aio_context(bs, new_context); } if (bs->io_limits_enabled) { - throttle_attach_aio_context(&bs->throttle_state, new_context); + throttle_timers_attach_aio_context(&bs->throttle_timers, new_context); } QLIST_FOREACH(ban, &bs->aio_notifiers, list) { diff --git a/block/Makefile.objs b/block/Makefile.objs index 0d8c2a4..c34fd7c 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -10,6 +10,7 @@ block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += raw-posix.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o block-obj-y += null.o mirror.o io.o +block-obj-y += throttle-groups.o block-obj-y += nbd.o nbd-client.o sheepdog.o block-obj-$(CONFIG_LIBISCSI) += iscsi.o diff --git a/block/blkdebug.c b/block/blkdebug.c index 1e92607..bc247f4 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -429,7 +429,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, /* Open the backing file */ assert(bs->file == NULL); ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-image"), options, "image", - flags | BDRV_O_PROTOCOL, false, &local_err); + bs, &child_file, false, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto out; diff --git a/block/blkverify.c b/block/blkverify.c index 438dff8..d277e63 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -125,7 +125,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, /* Open the raw file */ assert(bs->file == NULL); ret = bdrv_open_image(&bs->file, qemu_opt_get(opts, "x-raw"), options, - "raw", flags | BDRV_O_PROTOCOL, false, &local_err); + "raw", bs, &child_file, false, &local_err); if (ret < 0) { error_propagate(errp, local_err); goto fail; @@ -134,7 +134,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, /* Open the test file */ assert(s->test_file == NULL); ret = bdrv_open_image(&s->test_file, qemu_opt_get(opts, "x-image"), options, - "test", flags, false, &local_err); + "test", bs, &child_format, false, &local_err); if (ret < 0) { error_propagate(errp, local_err); s->test_file = NULL; @@ -23,9 +23,9 @@ */ #include "trace.h" -#include "sysemu/qtest.h" #include "block/blockjob.h" #include "block/block_int.h" +#include "block/throttle-groups.h" #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ @@ -65,7 +65,7 @@ void bdrv_set_io_limits(BlockDriverState *bs, { int i; - throttle_config(&bs->throttle_state, cfg); + throttle_group_config(bs, cfg); for (i = 0; i < 2; i++) { qemu_co_enter_next(&bs->throttled_reqs[i]); @@ -95,72 +95,33 @@ static bool bdrv_start_throttled_reqs(BlockDriverState *bs) void bdrv_io_limits_disable(BlockDriverState *bs) { bs->io_limits_enabled = false; - bdrv_start_throttled_reqs(bs); - - throttle_destroy(&bs->throttle_state); -} - -static void bdrv_throttle_read_timer_cb(void *opaque) -{ - BlockDriverState *bs = opaque; - qemu_co_enter_next(&bs->throttled_reqs[0]); -} - -static void bdrv_throttle_write_timer_cb(void *opaque) -{ - BlockDriverState *bs = opaque; - qemu_co_enter_next(&bs->throttled_reqs[1]); + throttle_group_unregister_bs(bs); } /* should be called before bdrv_set_io_limits if a limit is set */ -void bdrv_io_limits_enable(BlockDriverState *bs) +void bdrv_io_limits_enable(BlockDriverState *bs, const char *group) { - int clock_type = QEMU_CLOCK_REALTIME; - - if (qtest_enabled()) { - /* For testing block IO throttling only */ - clock_type = QEMU_CLOCK_VIRTUAL; - } assert(!bs->io_limits_enabled); - throttle_init(&bs->throttle_state, - bdrv_get_aio_context(bs), - clock_type, - bdrv_throttle_read_timer_cb, - bdrv_throttle_write_timer_cb, - bs); + throttle_group_register_bs(bs, group); bs->io_limits_enabled = true; } -/* This function makes an IO wait if needed - * - * @nb_sectors: the number of sectors of the IO - * @is_write: is the IO a write - */ -static void bdrv_io_limits_intercept(BlockDriverState *bs, - unsigned int bytes, - bool is_write) +void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group) { - /* does this io must wait */ - bool must_wait = throttle_schedule_timer(&bs->throttle_state, is_write); - - /* if must wait or any request of this type throttled queue the IO */ - if (must_wait || - !qemu_co_queue_empty(&bs->throttled_reqs[is_write])) { - qemu_co_queue_wait(&bs->throttled_reqs[is_write]); + /* this bs is not part of any group */ + if (!bs->throttle_state) { + return; } - /* the IO will be executed, do the accounting */ - throttle_account(&bs->throttle_state, is_write, bytes); - - - /* if the next request must wait -> do nothing */ - if (throttle_schedule_timer(&bs->throttle_state, is_write)) { + /* this bs is a part of the same group than the one we want */ + if (!g_strcmp0(throttle_group_get_name(bs), group)) { return; } - /* else queue next request for execution */ - qemu_co_queue_next(&bs->throttled_reqs[is_write]); + /* need to change the group this bs belong to */ + bdrv_io_limits_disable(bs); + bdrv_io_limits_enable(bs, group); } void bdrv_setup_io_funcs(BlockDriver *bdrv) @@ -967,7 +928,7 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, /* throttling disk I/O */ if (bs->io_limits_enabled) { - bdrv_io_limits_intercept(bs, bytes, false); + throttle_group_co_io_limits_intercept(bs, bytes, false); } /* Align read if necessary by padding qiov */ @@ -1297,7 +1258,7 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, /* throttling disk I/O */ if (bs->io_limits_enabled) { - bdrv_io_limits_intercept(bs, bytes, true); + throttle_group_co_io_limits_intercept(bs, bytes, true); } /* diff --git a/block/qapi.c b/block/qapi.c index 18d2b95..a738148 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -24,6 +24,7 @@ #include "block/qapi.h" #include "block/block_int.h" +#include "block/throttle-groups.h" #include "block/write-threshold.h" #include "qmp-commands.h" #include "qapi-visit.h" @@ -65,7 +66,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) if (bs->io_limits_enabled) { ThrottleConfig cfg; - throttle_get_config(&bs->throttle_state, &cfg); + + throttle_group_get_config(bs, &cfg); + info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg; @@ -90,6 +93,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) info->has_iops_size = cfg.op_size; info->iops_size = cfg.op_size; + + info->has_group = true; + info->group = g_strdup(throttle_group_get_name(bs)); } info->write_threshold = bdrv_write_threshold_get(bs); diff --git a/block/qcow2.c b/block/qcow2.c index f7b4cc6..c4f6938 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -483,9 +483,11 @@ static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = { [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2, }; -static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size, +static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, + uint64_t *l2_cache_size, uint64_t *refcount_cache_size, Error **errp) { + BDRVQcowState *s = bs->opaque; uint64_t combined_cache_size; bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; @@ -525,7 +527,9 @@ static void read_cache_sizes(QemuOpts *opts, uint64_t *l2_cache_size, } } else { if (!l2_cache_size_set && !refcount_cache_size_set) { - *l2_cache_size = DEFAULT_L2_CACHE_BYTE_SIZE; + *l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE, + (uint64_t)DEFAULT_L2_CACHE_CLUSTERS + * s->cluster_size); *refcount_cache_size = *l2_cache_size / DEFAULT_L2_REFCOUNT_SIZE_RATIO; } else if (!l2_cache_size_set) { @@ -803,7 +807,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - read_cache_sizes(opts, &l2_cache_size, &refcount_cache_size, &local_err); + read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size, + &local_err); if (local_err) { error_propagate(errp, local_err); ret = -EINVAL; diff --git a/block/qcow2.h b/block/qcow2.h index 0076512..5936d29 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -62,11 +62,14 @@ #define MIN_CLUSTER_BITS 9 #define MAX_CLUSTER_BITS 21 -#define MIN_L2_CACHE_SIZE 1 /* cluster */ +/* Must be at least 2 to cover COW */ +#define MIN_L2_CACHE_SIZE 2 /* clusters */ /* Must be at least 4 to cover all cases of refcount table growth */ #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ +/* Whichever is more */ +#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */ #define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */ /* The refblock cache needs only a fourth of the L2 cache size to cover as many diff --git a/block/quorum.c b/block/quorum.c index f91ef75..77e55b2 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -866,25 +866,18 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, Error *local_err = NULL; QemuOpts *opts = NULL; bool *opened; - QDict *sub = NULL; - QList *list = NULL; - const QListEntry *lentry; int i; int ret = 0; qdict_flatten(options); - qdict_extract_subqdict(options, &sub, "children."); - qdict_array_split(sub, &list); - if (qdict_size(sub)) { - error_setg(&local_err, "Invalid option children.%s", - qdict_first(sub)->key); + /* count how many different children are present */ + s->num_children = qdict_array_entries(options, "children."); + if (s->num_children < 0) { + error_setg(&local_err, "Option children is not a valid array"); ret = -EINVAL; goto exit; } - - /* count how many different children are present */ - s->num_children = qlist_size(list); if (s->num_children < 2) { error_setg(&local_err, "Number of provided children must be greater than 1"); @@ -937,37 +930,17 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, s->bs = g_new0(BlockDriverState *, s->num_children); opened = g_new0(bool, s->num_children); - for (i = 0, lentry = qlist_first(list); lentry; - lentry = qlist_next(lentry), i++) { - QDict *d; - QString *string; - - switch (qobject_type(lentry->value)) - { - /* List of options */ - case QTYPE_QDICT: - d = qobject_to_qdict(lentry->value); - QINCREF(d); - ret = bdrv_open(&s->bs[i], NULL, NULL, d, flags, NULL, - &local_err); - break; - - /* QMP reference */ - case QTYPE_QSTRING: - string = qobject_to_qstring(lentry->value); - ret = bdrv_open(&s->bs[i], NULL, qstring_get_str(string), NULL, - flags, NULL, &local_err); - break; - - default: - error_setg(&local_err, "Specification of child block device %i " - "is invalid", i); - ret = -EINVAL; - } + for (i = 0; i < s->num_children; i++) { + char indexstr[32]; + ret = snprintf(indexstr, 32, "children.%d", i); + assert(ret < 32); + ret = bdrv_open_image(&s->bs[i], NULL, options, indexstr, bs, + &child_format, false, &local_err); if (ret < 0) { goto close_exit; } + opened[i] = true; } @@ -990,8 +963,6 @@ exit: if (local_err) { error_propagate(errp, local_err); } - QDECREF(list); - QDECREF(sub); return ret; } diff --git a/block/raw-posix.c b/block/raw-posix.c index 2990e95..44ade8c 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -1848,8 +1848,9 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs, *pnum = nb_sectors; ret = BDRV_BLOCK_DATA; } else if (data == start) { - /* On a data extent, compute sectors to the end of the extent. */ - *pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE); + /* On a data extent, compute sectors to the end of the extent, + * possibly including a partial sector at EOF. */ + *pnum = MIN(nb_sectors, DIV_ROUND_UP(hole - start, BDRV_SECTOR_SIZE)); ret = BDRV_BLOCK_DATA; } else { /* On a hole, compute sectors to the beginning of the next extent. */ diff --git a/block/throttle-groups.c b/block/throttle-groups.c new file mode 100644 index 0000000..efc462f --- /dev/null +++ b/block/throttle-groups.c @@ -0,0 +1,496 @@ +/* + * QEMU block throttling group infrastructure + * + * Copyright (C) Nodalink, EURL. 2014 + * Copyright (C) Igalia, S.L. 2015 + * + * Authors: + * Benoît Canet <benoit.canet@nodalink.com> + * Alberto Garcia <berto@igalia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "block/throttle-groups.h" +#include "qemu/queue.h" +#include "qemu/thread.h" +#include "sysemu/qtest.h" + +/* The ThrottleGroup structure (with its ThrottleState) is shared + * among different BlockDriverState and it's independent from + * AioContext, so in order to use it from different threads it needs + * its own locking. + * + * This locking is however handled internally in this file, so it's + * mostly transparent to outside users (but see the documentation in + * throttle_groups_lock()). + * + * The whole ThrottleGroup structure is private and invisible to + * outside users, that only use it through its ThrottleState. + * + * In addition to the ThrottleGroup structure, BlockDriverState has + * fields that need to be accessed by other members of the group and + * therefore also need to be protected by this lock. Once a BDS is + * registered in a group those fields can be accessed by other threads + * any time. + * + * Again, all this is handled internally and is mostly transparent to + * the outside. The 'throttle_timers' field however has an additional + * constraint because it may be temporarily invalid (see for example + * bdrv_set_aio_context()). Therefore in this file a thread will + * access some other BDS's timers only after verifying that that BDS + * has throttled requests in the queue. + */ +typedef struct ThrottleGroup { + char *name; /* This is constant during the lifetime of the group */ + + QemuMutex lock; /* This lock protects the following four fields */ + ThrottleState ts; + QLIST_HEAD(, BlockDriverState) head; + BlockDriverState *tokens[2]; + bool any_timer_armed[2]; + + /* These two are protected by the global throttle_groups_lock */ + unsigned refcount; + QTAILQ_ENTRY(ThrottleGroup) list; +} ThrottleGroup; + +static QemuMutex throttle_groups_lock; +static QTAILQ_HEAD(, ThrottleGroup) throttle_groups = + QTAILQ_HEAD_INITIALIZER(throttle_groups); + +/* Increments the reference count of a ThrottleGroup given its name. + * + * If no ThrottleGroup is found with the given name a new one is + * created. + * + * @name: the name of the ThrottleGroup + * @ret: the ThrottleGroup + */ +static ThrottleGroup *throttle_group_incref(const char *name) +{ + ThrottleGroup *tg = NULL; + ThrottleGroup *iter; + + qemu_mutex_lock(&throttle_groups_lock); + + /* Look for an existing group with that name */ + QTAILQ_FOREACH(iter, &throttle_groups, list) { + if (!strcmp(name, iter->name)) { + tg = iter; + break; + } + } + + /* Create a new one if not found */ + if (!tg) { + tg = g_new0(ThrottleGroup, 1); + tg->name = g_strdup(name); + qemu_mutex_init(&tg->lock); + throttle_init(&tg->ts); + QLIST_INIT(&tg->head); + + QTAILQ_INSERT_TAIL(&throttle_groups, tg, list); + } + + tg->refcount++; + + qemu_mutex_unlock(&throttle_groups_lock); + + return tg; +} + +/* Decrease the reference count of a ThrottleGroup. + * + * When the reference count reaches zero the ThrottleGroup is + * destroyed. + * + * @tg: The ThrottleGroup to unref + */ +static void throttle_group_unref(ThrottleGroup *tg) +{ + qemu_mutex_lock(&throttle_groups_lock); + if (--tg->refcount == 0) { + QTAILQ_REMOVE(&throttle_groups, tg, list); + qemu_mutex_destroy(&tg->lock); + g_free(tg->name); + g_free(tg); + } + qemu_mutex_unlock(&throttle_groups_lock); +} + +/* Get the name from a BlockDriverState's ThrottleGroup. The name (and + * the pointer) is guaranteed to remain constant during the lifetime + * of the group. + * + * @bs: a BlockDriverState that is member of a throttling group + * @ret: the name of the group. + */ +const char *throttle_group_get_name(BlockDriverState *bs) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + return tg->name; +} + +/* Return the next BlockDriverState in the round-robin sequence, + * simulating a circular list. + * + * This assumes that tg->lock is held. + * + * @bs: the current BlockDriverState + * @ret: the next BlockDriverState in the sequence + */ +static BlockDriverState *throttle_group_next_bs(BlockDriverState *bs) +{ + ThrottleState *ts = bs->throttle_state; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + BlockDriverState *next = QLIST_NEXT(bs, round_robin); + + if (!next) { + return QLIST_FIRST(&tg->head); + } + + return next; +} + +/* Return the next BlockDriverState in the round-robin sequence with + * pending I/O requests. + * + * This assumes that tg->lock is held. + * + * @bs: the current BlockDriverState + * @is_write: the type of operation (read/write) + * @ret: the next BlockDriverState with pending requests, or bs + * if there is none. + */ +static BlockDriverState *next_throttle_token(BlockDriverState *bs, + bool is_write) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + BlockDriverState *token, *start; + + start = token = tg->tokens[is_write]; + + /* get next bs round in round robin style */ + token = throttle_group_next_bs(token); + while (token != start && !token->pending_reqs[is_write]) { + token = throttle_group_next_bs(token); + } + + /* If no IO are queued for scheduling on the next round robin token + * then decide the token is the current bs because chances are + * the current bs get the current request queued. + */ + if (token == start && !token->pending_reqs[is_write]) { + token = bs; + } + + return token; +} + +/* Check if the next I/O request for a BlockDriverState needs to be + * throttled or not. If there's no timer set in this group, set one + * and update the token accordingly. + * + * This assumes that tg->lock is held. + * + * @bs: the current BlockDriverState + * @is_write: the type of operation (read/write) + * @ret: whether the I/O request needs to be throttled or not + */ +static bool throttle_group_schedule_timer(BlockDriverState *bs, + bool is_write) +{ + ThrottleState *ts = bs->throttle_state; + ThrottleTimers *tt = &bs->throttle_timers; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + bool must_wait; + + /* Check if any of the timers in this group is already armed */ + if (tg->any_timer_armed[is_write]) { + return true; + } + + must_wait = throttle_schedule_timer(ts, tt, is_write); + + /* If a timer just got armed, set bs as the current token */ + if (must_wait) { + tg->tokens[is_write] = bs; + tg->any_timer_armed[is_write] = true; + } + + return must_wait; +} + +/* Look for the next pending I/O request and schedule it. + * + * This assumes that tg->lock is held. + * + * @bs: the current BlockDriverState + * @is_write: the type of operation (read/write) + */ +static void schedule_next_request(BlockDriverState *bs, bool is_write) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + bool must_wait; + BlockDriverState *token; + + /* Check if there's any pending request to schedule next */ + token = next_throttle_token(bs, is_write); + if (!token->pending_reqs[is_write]) { + return; + } + + /* Set a timer for the request if it needs to be throttled */ + must_wait = throttle_group_schedule_timer(token, is_write); + + /* If it doesn't have to wait, queue it for immediate execution */ + if (!must_wait) { + /* Give preference to requests from the current bs */ + if (qemu_in_coroutine() && + qemu_co_queue_next(&bs->throttled_reqs[is_write])) { + token = bs; + } else { + ThrottleTimers *tt = &token->throttle_timers; + int64_t now = qemu_clock_get_ns(tt->clock_type); + timer_mod(tt->timers[is_write], now + 1); + tg->any_timer_armed[is_write] = true; + } + tg->tokens[is_write] = token; + } +} + +/* Check if an I/O request needs to be throttled, wait and set a timer + * if necessary, and schedule the next request using a round robin + * algorithm. + * + * @bs: the current BlockDriverState + * @bytes: the number of bytes for this I/O + * @is_write: the type of operation (read/write) + */ +void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, + unsigned int bytes, + bool is_write) +{ + bool must_wait; + BlockDriverState *token; + + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + qemu_mutex_lock(&tg->lock); + + /* First we check if this I/O has to be throttled. */ + token = next_throttle_token(bs, is_write); + must_wait = throttle_group_schedule_timer(token, is_write); + + /* Wait if there's a timer set or queued requests of this type */ + if (must_wait || bs->pending_reqs[is_write]) { + bs->pending_reqs[is_write]++; + qemu_mutex_unlock(&tg->lock); + qemu_co_queue_wait(&bs->throttled_reqs[is_write]); + qemu_mutex_lock(&tg->lock); + bs->pending_reqs[is_write]--; + } + + /* The I/O will be executed, so do the accounting */ + throttle_account(bs->throttle_state, is_write, bytes); + + /* Schedule the next request */ + schedule_next_request(bs, is_write); + + qemu_mutex_unlock(&tg->lock); +} + +/* Update the throttle configuration for a particular group. Similar + * to throttle_config(), but guarantees atomicity within the + * throttling group. + * + * @bs: a BlockDriverState that is member of the group + * @cfg: the configuration to set + */ +void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg) +{ + ThrottleTimers *tt = &bs->throttle_timers; + ThrottleState *ts = bs->throttle_state; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + qemu_mutex_lock(&tg->lock); + throttle_config(ts, tt, cfg); + /* throttle_config() cancels the timers */ + tg->any_timer_armed[0] = tg->any_timer_armed[1] = false; + qemu_mutex_unlock(&tg->lock); +} + +/* Get the throttle configuration from a particular group. Similar to + * throttle_get_config(), but guarantees atomicity within the + * throttling group. + * + * @bs: a BlockDriverState that is member of the group + * @cfg: the configuration will be written here + */ +void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg) +{ + ThrottleState *ts = bs->throttle_state; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + qemu_mutex_lock(&tg->lock); + throttle_get_config(ts, cfg); + qemu_mutex_unlock(&tg->lock); +} + +/* ThrottleTimers callback. This wakes up a request that was waiting + * because it had been throttled. + * + * @bs: the BlockDriverState whose request had been throttled + * @is_write: the type of operation (read/write) + */ +static void timer_cb(BlockDriverState *bs, bool is_write) +{ + ThrottleState *ts = bs->throttle_state; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + bool empty_queue; + + /* The timer has just been fired, so we can update the flag */ + qemu_mutex_lock(&tg->lock); + tg->any_timer_armed[is_write] = false; + qemu_mutex_unlock(&tg->lock); + + /* Run the request that was waiting for this timer */ + empty_queue = !qemu_co_enter_next(&bs->throttled_reqs[is_write]); + + /* If the request queue was empty then we have to take care of + * scheduling the next one */ + if (empty_queue) { + qemu_mutex_lock(&tg->lock); + schedule_next_request(bs, is_write); + qemu_mutex_unlock(&tg->lock); + } +} + +static void read_timer_cb(void *opaque) +{ + timer_cb(opaque, false); +} + +static void write_timer_cb(void *opaque) +{ + timer_cb(opaque, true); +} + +/* Register a BlockDriverState in the throttling group, also + * initializing its timers and updating its throttle_state pointer to + * point to it. If a throttling group with that name does not exist + * yet, it will be created. + * + * @bs: the BlockDriverState to insert + * @groupname: the name of the group + */ +void throttle_group_register_bs(BlockDriverState *bs, const char *groupname) +{ + int i; + ThrottleGroup *tg = throttle_group_incref(groupname); + int clock_type = QEMU_CLOCK_REALTIME; + + if (qtest_enabled()) { + /* For testing block IO throttling only */ + clock_type = QEMU_CLOCK_VIRTUAL; + } + + bs->throttle_state = &tg->ts; + + qemu_mutex_lock(&tg->lock); + /* If the ThrottleGroup is new set this BlockDriverState as the token */ + for (i = 0; i < 2; i++) { + if (!tg->tokens[i]) { + tg->tokens[i] = bs; + } + } + + QLIST_INSERT_HEAD(&tg->head, bs, round_robin); + + throttle_timers_init(&bs->throttle_timers, + bdrv_get_aio_context(bs), + clock_type, + read_timer_cb, + write_timer_cb, + bs); + + qemu_mutex_unlock(&tg->lock); +} + +/* Unregister a BlockDriverState from its group, removing it from the + * list, destroying the timers and setting the throttle_state pointer + * to NULL. + * + * The group will be destroyed if it's empty after this operation. + * + * @bs: the BlockDriverState to remove + */ +void throttle_group_unregister_bs(BlockDriverState *bs) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + int i; + + qemu_mutex_lock(&tg->lock); + for (i = 0; i < 2; i++) { + if (tg->tokens[i] == bs) { + BlockDriverState *token = throttle_group_next_bs(bs); + /* Take care of the case where this is the last bs in the group */ + if (token == bs) { + token = NULL; + } + tg->tokens[i] = token; + } + } + + /* remove the current bs from the list */ + QLIST_REMOVE(bs, round_robin); + throttle_timers_destroy(&bs->throttle_timers); + qemu_mutex_unlock(&tg->lock); + + throttle_group_unref(tg); + bs->throttle_state = NULL; +} + +/* Acquire the lock of this throttling group. + * + * You won't normally need to use this. None of the functions from the + * ThrottleGroup API require you to acquire the lock since all of them + * deal with it internally. + * + * This should only be used in exceptional cases when you want to + * access the protected fields of a BlockDriverState directly + * (e.g. bdrv_swap()). + * + * @bs: a BlockDriverState that is member of the group + */ +void throttle_group_lock(BlockDriverState *bs) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + qemu_mutex_lock(&tg->lock); +} + +/* Release the lock of this throttling group. + * + * See the comments in throttle_group_lock(). + */ +void throttle_group_unlock(BlockDriverState *bs) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + qemu_mutex_unlock(&tg->lock); +} + +static void throttle_groups_init(void) +{ + qemu_mutex_init(&throttle_groups_lock); +} + +block_init(throttle_groups_init); diff --git a/block/vmdk.c b/block/vmdk.c index b66745d..be9263a 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -321,37 +321,13 @@ static int vmdk_is_cid_valid(BlockDriverState *bs) return 1; } -/* Queue extents, if any, for reopen() */ +/* We have nothing to do for VMDK reopen, stubs just return success */ static int vmdk_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { - BDRVVmdkState *s; - int ret = -1; - int i; - VmdkExtent *e; - assert(state != NULL); assert(state->bs != NULL); - - if (queue == NULL) { - error_setg(errp, "No reopen queue for VMDK extents"); - goto exit; - } - - s = state->bs->opaque; - - assert(s != NULL); - - for (i = 0; i < s->num_extents; i++) { - e = &s->extents[i]; - if (e->file != state->bs->file) { - bdrv_reopen_queue(queue, e->file, state->flags); - } - } - ret = 0; - -exit: - return ret; + return 0; } static int vmdk_parent_open(BlockDriverState *bs) @@ -543,7 +519,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs, } static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, - Error **errp); + QDict *options, Error **errp); static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset, Error **errp) @@ -582,7 +558,7 @@ static char *vmdk_read_desc(BlockDriverState *file, uint64_t desc_offset, static int vmdk_open_vmdk4(BlockDriverState *bs, BlockDriverState *file, - int flags, Error **errp) + int flags, QDict *options, Error **errp) { int ret; uint32_t magic; @@ -606,7 +582,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, if (!buf) { return -EINVAL; } - ret = vmdk_open_desc_file(bs, flags, buf, errp); + ret = vmdk_open_desc_file(bs, flags, buf, options, errp); g_free(buf); return ret; } @@ -763,7 +739,7 @@ static int vmdk_parse_description(const char *desc, const char *opt_name, /* Open an extent file and append to bs array */ static int vmdk_open_sparse(BlockDriverState *bs, BlockDriverState *file, int flags, - char *buf, Error **errp) + char *buf, QDict *options, Error **errp) { uint32_t magic; @@ -773,7 +749,7 @@ static int vmdk_open_sparse(BlockDriverState *bs, return vmdk_open_vmfs_sparse(bs, file, flags, errp); break; case VMDK4_MAGIC: - return vmdk_open_vmdk4(bs, file, flags, errp); + return vmdk_open_vmdk4(bs, file, flags, options, errp); break; default: error_setg(errp, "Image not in VMDK format"); @@ -783,7 +759,8 @@ static int vmdk_open_sparse(BlockDriverState *bs, } static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, - const char *desc_file_path, Error **errp) + const char *desc_file_path, QDict *options, + Error **errp) { int ret; int matches; @@ -797,6 +774,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, BlockDriverState *extent_file; BDRVVmdkState *s = bs->opaque; VmdkExtent *extent; + char extent_opt_prefix[32]; while (*p) { /* parse extent line in one of below formats: @@ -846,8 +824,12 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, extent_path = g_malloc0(PATH_MAX); path_combine(extent_path, PATH_MAX, desc_file_path, fname); extent_file = NULL; - ret = bdrv_open(&extent_file, extent_path, NULL, NULL, - bs->open_flags | BDRV_O_PROTOCOL, NULL, errp); + + ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents); + assert(ret < 32); + + ret = bdrv_open_image(&extent_file, extent_path, options, + extent_opt_prefix, bs, &child_file, false, errp); g_free(extent_path); if (ret) { return ret; @@ -870,7 +852,8 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, if (!buf) { ret = -EINVAL; } else { - ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, errp); + ret = vmdk_open_sparse(bs, extent_file, bs->open_flags, buf, + options, errp); } g_free(buf); if (ret) { @@ -898,7 +881,7 @@ next_line: } static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, - Error **errp) + QDict *options, Error **errp) { int ret; char ct[128]; @@ -920,7 +903,7 @@ static int vmdk_open_desc_file(BlockDriverState *bs, int flags, char *buf, } s->create_type = g_strdup(ct); s->desc_offset = 0; - ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, errp); + ret = vmdk_parse_extents(buf, bs, bs->file->exact_filename, options, errp); exit: return ret; } @@ -942,11 +925,11 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, switch (magic) { case VMDK3_MAGIC: case VMDK4_MAGIC: - ret = vmdk_open_sparse(bs, bs->file, flags, buf, errp); + ret = vmdk_open_sparse(bs, bs->file, flags, buf, options, errp); s->desc_offset = 0x200; break; default: - ret = vmdk_open_desc_file(bs, flags, buf, errp); + ret = vmdk_open_desc_file(bs, flags, buf, options, errp); break; } if (ret) { @@ -1248,6 +1231,17 @@ static VmdkExtent *find_extent(BDRVVmdkState *s, return NULL; } +static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent, + int64_t sector_num) +{ + uint64_t index_in_cluster, extent_begin_sector, extent_relative_sector_num; + + extent_begin_sector = extent->end_sector - extent->sectors; + extent_relative_sector_num = sector_num - extent_begin_sector; + index_in_cluster = extent_relative_sector_num % extent->cluster_sectors; + return index_in_cluster; +} + static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { @@ -1285,7 +1279,7 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs, break; } - index_in_cluster = sector_num % extent->cluster_sectors; + index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); n = extent->cluster_sectors - index_in_cluster; if (n > nb_sectors) { n = nb_sectors; @@ -1413,7 +1407,6 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, BDRVVmdkState *s = bs->opaque; int ret; uint64_t n, index_in_cluster; - uint64_t extent_begin_sector, extent_relative_sector_num; VmdkExtent *extent = NULL; uint64_t cluster_offset; @@ -1425,9 +1418,7 @@ static int vmdk_read(BlockDriverState *bs, int64_t sector_num, ret = get_cluster_offset(bs, extent, NULL, sector_num << 9, false, &cluster_offset, 0, 0); - extent_begin_sector = extent->end_sector - extent->sectors; - extent_relative_sector_num = sector_num - extent_begin_sector; - index_in_cluster = extent_relative_sector_num % extent->cluster_sectors; + index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); n = extent->cluster_sectors - index_in_cluster; if (n > nb_sectors) { n = nb_sectors; @@ -1489,7 +1480,6 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, VmdkExtent *extent = NULL; int ret; int64_t index_in_cluster, n; - uint64_t extent_begin_sector, extent_relative_sector_num; uint64_t cluster_offset; VmdkMetaData m_data; @@ -1505,9 +1495,7 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num, if (!extent) { return -EIO; } - extent_begin_sector = extent->end_sector - extent->sectors; - extent_relative_sector_num = sector_num - extent_begin_sector; - index_in_cluster = extent_relative_sector_num % extent->cluster_sectors; + index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num); n = extent->cluster_sectors - index_in_cluster; if (n > nb_sectors) { n = nb_sectors; diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 85cda4c..0d9df47 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -43,7 +43,7 @@ void qmp_nbd_server_start(SocketAddress *addr, Error **errp) server_fd = socket_listen(addr, errp); if (server_fd != -1) { - qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL); + qemu_set_fd_handler(server_fd, nbd_accept, NULL, NULL); } } @@ -129,7 +129,7 @@ void qmp_nbd_server_stop(Error **errp) } if (server_fd != -1) { - qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(server_fd, NULL, NULL, NULL); close(server_fd); server_fd = -1; } @@ -34,6 +34,7 @@ #include "sysemu/blockdev.h" #include "hw/block/block.h" #include "block/blockjob.h" +#include "block/throttle-groups.h" #include "monitor/monitor.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -357,6 +358,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, const char *id; bool has_driver_specific_opts; BlockdevDetectZeroesOptions detect_zeroes; + const char *throttling_group; /* Check common options by copying from bs_opts to opts, all other options * stay in bs_opts for processing by bdrv_open(). */ @@ -391,13 +393,13 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, } } - if (qemu_opt_get_bool(opts, "cache.writeback", true)) { + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true)) { bdrv_flags |= BDRV_O_CACHE_WB; } - if (qemu_opt_get_bool(opts, "cache.direct", false)) { + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) { bdrv_flags |= BDRV_O_NOCACHE; } - if (qemu_opt_get_bool(opts, "cache.no-flush", false)) { + if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { bdrv_flags |= BDRV_O_NO_FLUSH; } @@ -459,6 +461,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0); + throttling_group = qemu_opt_get(opts, "throttling.group"); + if (!check_throttle_config(&cfg, &error)) { error_propagate(errp, error); goto early_err; @@ -547,7 +551,10 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, /* disk I/O throttling */ if (throttle_enabled(&cfg)) { - bdrv_io_limits_enable(bs); + if (!throttling_group) { + throttling_group = blk_name(blk); + } + bdrv_io_limits_enable(bs, throttling_group); bdrv_set_io_limits(bs, &cfg); } @@ -711,6 +718,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) { "iops_size", "throttling.iops-size" }, + { "group", "throttling.group" }, + { "readonly", "read-only" }, }; @@ -733,16 +742,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) } /* Specific options take precedence */ - if (!qemu_opt_get(all_opts, "cache.writeback")) { - qemu_opt_set_bool(all_opts, "cache.writeback", + if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) { + qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB, !!(flags & BDRV_O_CACHE_WB), &error_abort); } - if (!qemu_opt_get(all_opts, "cache.direct")) { - qemu_opt_set_bool(all_opts, "cache.direct", + if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) { + qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT, !!(flags & BDRV_O_NOCACHE), &error_abort); } - if (!qemu_opt_get(all_opts, "cache.no-flush")) { - qemu_opt_set_bool(all_opts, "cache.no-flush", + if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_NO_FLUSH)) { + qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_NO_FLUSH, !!(flags & BDRV_O_NO_FLUSH), &error_abort); } qemu_opt_unset(all_opts, "cache"); @@ -1951,7 +1960,9 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, bool has_iops_wr_max, int64_t iops_wr_max, bool has_iops_size, - int64_t iops_size, Error **errp) + int64_t iops_size, + bool has_group, + const char *group, Error **errp) { ThrottleConfig cfg; BlockDriverState *bs; @@ -2004,14 +2015,19 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - if (!bs->io_limits_enabled && throttle_enabled(&cfg)) { - bdrv_io_limits_enable(bs); - } else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) { - bdrv_io_limits_disable(bs); - } - - if (bs->io_limits_enabled) { + if (throttle_enabled(&cfg)) { + /* Enable I/O limits if they're not enabled yet, otherwise + * just update the throttling group. */ + if (!bs->io_limits_enabled) { + bdrv_io_limits_enable(bs, has_group ? group : device); + } else if (has_group) { + bdrv_io_limits_update_group(bs, group); + } + /* Set the new throttling configuration */ bdrv_set_io_limits(bs, &cfg); + } else if (bs->io_limits_enabled) { + /* If all throttling settings are set to 0, disable I/O limits */ + bdrv_io_limits_disable(bs); } aio_context_release(aio_context); @@ -3105,15 +3121,15 @@ QemuOptsList qemu_common_drive_opts = { .type = QEMU_OPT_STRING, .help = "discard operation (ignore/off, unmap/on)", },{ - .name = "cache.writeback", + .name = BDRV_OPT_CACHE_WB, .type = QEMU_OPT_BOOL, .help = "enables writeback mode for any caches", },{ - .name = "cache.direct", + .name = BDRV_OPT_CACHE_DIRECT, .type = QEMU_OPT_BOOL, .help = "enables use of O_DIRECT (bypass the host page cache)", },{ - .name = "cache.no-flush", + .name = BDRV_OPT_CACHE_NO_FLUSH, .type = QEMU_OPT_BOOL, .help = "ignore any flush requests for the device", },{ @@ -3189,6 +3205,10 @@ QemuOptsList qemu_common_drive_opts = { .type = QEMU_OPT_NUMBER, .help = "when limiting by iops max size of an I/O in bytes", },{ + .name = "throttling.group", + .type = QEMU_OPT_STRING, + .help = "name of the block throttling group", + },{ .name = "copy-on-read", .type = QEMU_OPT_BOOL, .help = "copy read data from backing file into image file", @@ -285,8 +285,6 @@ sysconfdir="\${prefix}/etc" local_statedir="\${prefix}/var" confsuffix="/qemu" slirp="yes" -fmod_lib="" -fmod_inc="" oss_lib="" bsd="no" linux="no" @@ -437,6 +435,14 @@ EOF compile_object } +check_include() { +cat > $TMPC <<EOF +#include <$1> +int main(void) { return 0; } +EOF + compile_object +} + write_c_skeleton() { cat > $TMPC <<EOF int main(void) { return 0; } @@ -564,24 +570,28 @@ case $targetos in CYGWIN*) mingw32="yes" QEMU_CFLAGS="-mno-cygwin $QEMU_CFLAGS" - audio_possible_drivers="winwave sdl" - audio_drv_list="winwave" + audio_possible_drivers="sdl" + audio_drv_list="sdl" ;; MINGW32*) mingw32="yes" - audio_possible_drivers="winwave dsound sdl fmod" - audio_drv_list="winwave" + audio_possible_drivers="dsound sdl" + if check_include dsound.h; then + audio_drv_list="dsound" + else + audio_drv_list="" + fi ;; GNU/kFreeBSD) bsd="yes" audio_drv_list="oss" - audio_possible_drivers="oss sdl esd pa" + audio_possible_drivers="oss sdl pa" ;; FreeBSD) bsd="yes" make="${MAKE-gmake}" audio_drv_list="oss" - audio_possible_drivers="oss sdl esd pa" + audio_possible_drivers="oss sdl pa" # needed for kinfo_getvmmap(3) in libutil.h LIBS="-lutil $LIBS" netmap="" # enable netmap autodetect @@ -591,14 +601,14 @@ DragonFly) bsd="yes" make="${MAKE-gmake}" audio_drv_list="oss" - audio_possible_drivers="oss sdl esd pa" + audio_possible_drivers="oss sdl pa" HOST_VARIANT_DIR="dragonfly" ;; NetBSD) bsd="yes" make="${MAKE-gmake}" audio_drv_list="oss" - audio_possible_drivers="oss sdl esd" + audio_possible_drivers="oss sdl" oss_lib="-lossaudio" HOST_VARIANT_DIR="netbsd" ;; @@ -606,7 +616,7 @@ OpenBSD) bsd="yes" make="${MAKE-gmake}" audio_drv_list="sdl" - audio_possible_drivers="sdl esd" + audio_possible_drivers="sdl" HOST_VARIANT_DIR="openbsd" ;; Darwin) @@ -619,7 +629,7 @@ Darwin) fi cocoa="yes" audio_drv_list="coreaudio" - audio_possible_drivers="coreaudio sdl fmod" + audio_possible_drivers="coreaudio sdl" LDFLAGS="-framework CoreFoundation -framework IOKit $LDFLAGS" libs_softmmu="-F/System/Library/Frameworks -framework Cocoa -framework IOKit $libs_softmmu" # Disable attempts to use ObjectiveC features in os/object.h since they @@ -674,15 +684,12 @@ Haiku) ;; *) audio_drv_list="oss" - audio_possible_drivers="oss alsa sdl esd pa" + audio_possible_drivers="oss alsa sdl pa" linux="yes" linux_user="yes" kvm="yes" vhost_net="yes" vhost_scsi="yes" - if [ "$cpu" = "i386" -o "$cpu" = "x86_64" -o "$cpu" = "x32" ] ; then - audio_possible_drivers="$audio_possible_drivers fmod" - fi QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$(pwd)/linux-headers $QEMU_INCLUDES" ;; esac @@ -847,10 +854,6 @@ for opt do ;; --enable-vnc) vnc="yes" ;; - --fmod-lib=*) fmod_lib="$optarg" - ;; - --fmod-inc=*) fmod_inc="$optarg" - ;; --oss-lib=*) oss_lib="$optarg" ;; --audio-drv-list=*) audio_drv_list="$optarg" @@ -1349,8 +1352,6 @@ Advanced options (experts only): --disable-guest-base disable GUEST_BASE support --enable-pie build Position Independent Executables --disable-pie do not build Position Independent Executables - --fmod-lib path to FMOD library - --fmod-inc path to FMOD includes --oss-lib path to OSS library --cpu=CPU Build for host CPU [$cpu] --disable-uuid disable uuid support @@ -2621,21 +2622,6 @@ for drv in $audio_drv_list; do libs_softmmu="-lasound $libs_softmmu" ;; - fmod) - if test -z $fmod_lib || test -z $fmod_inc; then - error_exit "You must specify path to FMOD library and headers" \ - "Example: --fmod-inc=/path/include/fmod --fmod-lib=/path/lib/libfmod-3.74.so" - fi - audio_drv_probe $drv fmod.h $fmod_lib "return FSOUND_GetVersion();" "-I $fmod_inc" - libs_softmmu="$fmod_lib $libs_softmmu" - ;; - - esd) - audio_drv_probe $drv esd.h -lesd 'return esd_play_stream(0, 0, "", 0);' - libs_softmmu="-lesd $libs_softmmu" - audio_pt_int="yes" - ;; - pa) audio_drv_probe $drv pulse/mainloop.h "-lpulse" \ "pa_mainloop *m = 0; pa_mainloop_free (m); return 0;" @@ -2660,11 +2646,6 @@ for drv in $audio_drv_list; do # XXX: Probes for CoreAudio, DirectSound, SDL(?) ;; - winwave) - libs_softmmu="-lwinmm $libs_softmmu" - audio_win_int="yes" - ;; - *) echo "$audio_possible_drivers" | grep -q "\<$drv\>" || { error_exit "Unknown driver '$drv' selected" \ @@ -4629,9 +4610,6 @@ echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak for drv in $audio_drv_list; do def=CONFIG_`echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]'` echo "$def=y" >> $config_host_mak - if test "$drv" = "fmod"; then - echo "FMOD_CFLAGS=-I$fmod_inc" >> $config_host_mak - fi done if test "$audio_pt_int" = "yes" ; then echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak @@ -480,6 +480,7 @@ static const VMStateDescription icount_vmstate_timers = { .name = "timer/icount", .version_id = 1, .minimum_version_id = 1, + .needed = icount_state_needed, .fields = (VMStateField[]) { VMSTATE_INT64(qemu_icount_bias, TimersState), VMSTATE_INT64(qemu_icount, TimersState), @@ -497,13 +498,9 @@ static const VMStateDescription vmstate_timers = { VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &icount_vmstate_timers, - .needed = icount_state_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &icount_vmstate_timers, + NULL } }; diff --git a/default-configs/mips-softmmu.mak b/default-configs/mips-softmmu.mak index fd0607d..44467c3 100644 --- a/default-configs/mips-softmmu.mak +++ b/default-configs/mips-softmmu.mak @@ -24,14 +24,9 @@ CONFIG_PIIX4=y CONFIG_IDE_ISA=y CONFIG_IDE_PIIX=y CONFIG_NE2000_ISA=y -CONFIG_RC4030=y -CONFIG_DP8393X=y -CONFIG_DS1225Y=y CONFIG_MIPSNET=y CONFIG_PFLASH_CFI01=y -CONFIG_G364FB=y CONFIG_I8259=y -CONFIG_JAZZ_LED=y CONFIG_MC146818RTC=y CONFIG_ISA_TESTDEV=y CONFIG_EMPTY_SLOT=y diff --git a/default-configs/mips64-softmmu.mak b/default-configs/mips64-softmmu.mak index b8c7910..66ed5f9 100644 --- a/default-configs/mips64-softmmu.mak +++ b/default-configs/mips64-softmmu.mak @@ -29,6 +29,7 @@ CONFIG_DP8393X=y CONFIG_DS1225Y=y CONFIG_MIPSNET=y CONFIG_PFLASH_CFI01=y +CONFIG_JAZZ=y CONFIG_G364FB=y CONFIG_I8259=y CONFIG_JAZZ_LED=y diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak index ae4274b..bfca2b2 100644 --- a/default-configs/mips64el-softmmu.mak +++ b/default-configs/mips64el-softmmu.mak @@ -31,6 +31,7 @@ CONFIG_DS1225Y=y CONFIG_MIPSNET=y CONFIG_PFLASH_CFI01=y CONFIG_FULONG=y +CONFIG_JAZZ=y CONFIG_G364FB=y CONFIG_I8259=y CONFIG_JAZZ_LED=y diff --git a/default-configs/mipsel-softmmu.mak b/default-configs/mipsel-softmmu.mak index 1e2374b..0162ef0 100644 --- a/default-configs/mipsel-softmmu.mak +++ b/default-configs/mipsel-softmmu.mak @@ -24,14 +24,9 @@ CONFIG_PIIX4=y CONFIG_IDE_ISA=y CONFIG_IDE_PIIX=y CONFIG_NE2000_ISA=y -CONFIG_RC4030=y -CONFIG_DP8393X=y -CONFIG_DS1225Y=y CONFIG_MIPSNET=y CONFIG_PFLASH_CFI01=y -CONFIG_G364FB=y CONFIG_I8259=y -CONFIG_JAZZ_LED=y CONFIG_MC146818RTC=y CONFIG_ISA_TESTDEV=y CONFIG_EMPTY_SLOT=y diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index f9e13f1..36e15de 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -4,3 +4,4 @@ CONFIG_VIRTIO=y CONFIG_SCLPCONSOLE=y CONFIG_S390_FLIC=y CONFIG_S390_FLIC_KVM=$(CONFIG_KVM) +CONFIG_WDT_DIAG288=y diff --git a/disas/mips.c b/disas/mips.c index 1afe0c5..32940fe 100644 --- a/disas/mips.c +++ b/disas/mips.c @@ -2238,6 +2238,8 @@ const struct mips_opcode mips_builtin_opcodes[] = {"ceil.l.s", "D,S", 0x4600000a, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I3|I33 }, {"ceil.w.d", "D,S", 0x4620000e, 0xffff003f, WR_D|RD_S|FP_S|FP_D, 0, I2 }, {"ceil.w.s", "D,S", 0x4600000e, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 }, +{"mfhc0", "t,G,H", 0x40400000, 0xffe007f8, LCD|WR_t|RD_C0, 0, I33}, +{"mthc0", "t,G,H", 0x40c00000, 0xffe007f8, COD|RD_t|WR_C0|WR_CC, 0, I33}, {"cfc0", "t,G", 0x40400000, 0xffe007ff, LCD|WR_t|RD_C0, 0, I1 }, {"cfc1", "t,G", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 }, {"cfc1", "t,S", 0x44400000, 0xffe007ff, LCD|WR_t|RD_C1|FP_S, 0, I1 }, @@ -2407,6 +2409,7 @@ const struct mips_opcode mips_builtin_opcodes[] = {"emt", "", 0x41600be1, 0xffffffff, TRAP, 0, MT32 }, {"emt", "t", 0x41600be1, 0xffe0ffff, TRAP|WR_t, 0, MT32 }, {"eret", "", 0x42000018, 0xffffffff, 0, 0, I3|I32 }, +{"eretnc", "", 0x42000058, 0xffffffff, 0, 0, I33}, {"evpe", "", 0x41600021, 0xffffffff, TRAP, 0, MT32 }, {"evpe", "t", 0x41600021, 0xffe0ffff, TRAP|WR_t, 0, MT32 }, {"ext", "t,r,+A,+C", 0x7c000000, 0xfc00003f, WR_t|RD_s, 0, I33 }, diff --git a/docs/migration.txt b/docs/migration.txt index 0492a45..f6df4be 100644 --- a/docs/migration.txt +++ b/docs/migration.txt @@ -257,6 +257,7 @@ const VMStateDescription vmstate_ide_drive_pio_state = { .minimum_version_id = 1, .pre_save = ide_drive_pio_pre_save, .post_load = ide_drive_pio_post_load, + .needed = ide_drive_pio_state_needed, .fields = (VMStateField[]) { VMSTATE_INT32(req_nb_sectors, IDEState), VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1, @@ -279,13 +280,9 @@ const VMStateDescription vmstate_ide_drive = { .... several fields .... VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_ide_drive_pio_state, - .needed = ide_drive_pio_state_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_ide_drive_pio_state, + NULL } }; diff --git a/docs/specs/rocker.txt b/docs/specs/rocker.txt index 1e7e1e1..0af5c61 100644 --- a/docs/specs/rocker.txt +++ b/docs/specs/rocker.txt @@ -420,6 +420,7 @@ Other properties for front-panel ports are available via DMA CMD descriptors: LEARNING 1 MAC address learning on port 1 = enabled 0 = disabled + PHYS_NAME <var> Physical port name (string) Set PORT_SETTINGS descriptor: @@ -454,6 +454,7 @@ static const VMStateDescription vmstate_cpu_common_exception_index = { .name = "cpu_common/exception_index", .version_id = 1, .minimum_version_id = 1, + .needed = cpu_common_exception_index_needed, .fields = (VMStateField[]) { VMSTATE_INT32(exception_index, CPUState), VMSTATE_END_OF_LIST() @@ -471,13 +472,9 @@ const VMStateDescription vmstate_cpu_common = { VMSTATE_UINT32(interrupt_request, CPUState), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_cpu_common_exception_index, - .needed = cpu_common_exception_index_needed, - } , { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_cpu_common_exception_index, + NULL } }; @@ -3348,14 +3345,20 @@ bool cpu_physical_memory_is_io(hwaddr phys_addr) return res; } -void qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) +int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) { RAMBlock *block; + int ret = 0; rcu_read_lock(); QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { - func(block->host, block->offset, block->used_length, opaque); + ret = func(block->idstr, block->host, block->offset, + block->used_length, opaque); + if (ret) { + break; + } } rcu_read_unlock(); + return ret; } #endif diff --git a/hmp-commands.hx b/hmp-commands.hx index 3d7dfcc..d3b7932 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1799,5 +1799,29 @@ show available trace events and their state ETEXI STEXI +@item rocker @var{name} +@findex rocker +Show Rocker(s) +ETEXI + +STEXI +@item rocker_ports @var{name} +@findex rocker_ports +Show Rocker ports +ETEXI + +STEXI +@item rocker_of_dpa_flows @var{name} [@var{tbl_id}] +@findex rocker_of_dpa_flows +Show Rocker OF-DPA flow tables +ETEXI + +STEXI +@item rocker_of_dpa_groups @var{name} [@var{type}] +@findex rocker_of_dpa_groups +Show Rocker OF-DPA groups +ETEXI + +STEXI @end table ETEXI @@ -15,6 +15,7 @@ #include "hmp.h" #include "net/net.h" +#include "net/eth.h" #include "sysemu/char.h" #include "sysemu/block-backend.h" #include "qemu/option.h" @@ -399,7 +400,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info, " iops_max=%" PRId64 " iops_rd_max=%" PRId64 " iops_wr_max=%" PRId64 - " iops_size=%" PRId64 "\n", + " iops_size=%" PRId64 + " group=%s\n", inserted->bps, inserted->bps_rd, inserted->bps_wr, @@ -412,7 +414,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info, inserted->iops_max, inserted->iops_rd_max, inserted->iops_wr_max, - inserted->iops_size); + inserted->iops_size, + inserted->group); } if (verbose) { @@ -1356,7 +1359,9 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) false, 0, false, /* No default I/O size */ - 0, &err); + 0, + false, + NULL, &err); hmp_handle_error(mon, &err); } @@ -1999,3 +2004,305 @@ void hmp_qom_set(Monitor *mon, const QDict *qdict) } hmp_handle_error(mon, &err); } + +void hmp_rocker(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + RockerSwitch *rocker; + Error *errp = NULL; + + rocker = qmp_query_rocker(name, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, "name: %s\n", rocker->name); + monitor_printf(mon, "id: 0x%" PRIx64 "\n", rocker->id); + monitor_printf(mon, "ports: %d\n", rocker->ports); + + qapi_free_RockerSwitch(rocker); +} + +void hmp_rocker_ports(Monitor *mon, const QDict *qdict) +{ + RockerPortList *list, *port; + const char *name = qdict_get_str(qdict, "name"); + Error *errp = NULL; + + list = qmp_query_rocker_ports(name, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, " ena/ speed/ auto\n"); + monitor_printf(mon, " port link duplex neg?\n"); + + for (port = list; port; port = port->next) { + monitor_printf(mon, "%10s %-4s %-3s %2s %-3s\n", + port->value->name, + port->value->enabled ? port->value->link_up ? + "up" : "down" : "!ena", + port->value->speed == 10000 ? "10G" : "??", + port->value->duplex ? "FD" : "HD", + port->value->autoneg ? "Yes" : "No"); + } + + qapi_free_RockerPortList(list); +} + +void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict) +{ + RockerOfDpaFlowList *list, *info; + const char *name = qdict_get_str(qdict, "name"); + uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1); + Error *errp = NULL; + + list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, "prio tbl hits key(mask) --> actions\n"); + + for (info = list; info; info = info->next) { + RockerOfDpaFlow *flow = info->value; + RockerOfDpaFlowKey *key = flow->key; + RockerOfDpaFlowMask *mask = flow->mask; + RockerOfDpaFlowAction *action = flow->action; + + if (flow->hits) { + monitor_printf(mon, "%-4d %-3d %-4" PRIu64, + key->priority, key->tbl_id, flow->hits); + } else { + monitor_printf(mon, "%-4d %-3d ", + key->priority, key->tbl_id); + } + + if (key->has_in_pport) { + monitor_printf(mon, " pport %d", key->in_pport); + if (mask->has_in_pport) { + monitor_printf(mon, "(0x%x)", mask->in_pport); + } + } + + if (key->has_vlan_id) { + monitor_printf(mon, " vlan %d", + key->vlan_id & VLAN_VID_MASK); + if (mask->has_vlan_id) { + monitor_printf(mon, "(0x%x)", mask->vlan_id); + } + } + + if (key->has_tunnel_id) { + monitor_printf(mon, " tunnel %d", key->tunnel_id); + if (mask->has_tunnel_id) { + monitor_printf(mon, "(0x%x)", mask->tunnel_id); + } + } + + if (key->has_eth_type) { + switch (key->eth_type) { + case 0x0806: + monitor_printf(mon, " ARP"); + break; + case 0x0800: + monitor_printf(mon, " IP"); + break; + case 0x86dd: + monitor_printf(mon, " IPv6"); + break; + case 0x8809: + monitor_printf(mon, " LACP"); + break; + case 0x88cc: + monitor_printf(mon, " LLDP"); + break; + default: + monitor_printf(mon, " eth type 0x%04x", key->eth_type); + break; + } + } + + if (key->has_eth_src) { + if ((strcmp(key->eth_src, "01:00:00:00:00:00") == 0) && + (mask->has_eth_src) && + (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " src <any mcast/bcast>"); + } else if ((strcmp(key->eth_src, "00:00:00:00:00:00") == 0) && + (mask->has_eth_src) && + (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " src <any ucast>"); + } else { + monitor_printf(mon, " src %s", key->eth_src); + if (mask->has_eth_src) { + monitor_printf(mon, "(%s)", mask->eth_src); + } + } + } + + if (key->has_eth_dst) { + if ((strcmp(key->eth_dst, "01:00:00:00:00:00") == 0) && + (mask->has_eth_dst) && + (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " dst <any mcast/bcast>"); + } else if ((strcmp(key->eth_dst, "00:00:00:00:00:00") == 0) && + (mask->has_eth_dst) && + (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " dst <any ucast>"); + } else { + monitor_printf(mon, " dst %s", key->eth_dst); + if (mask->has_eth_dst) { + monitor_printf(mon, "(%s)", mask->eth_dst); + } + } + } + + if (key->has_ip_proto) { + monitor_printf(mon, " proto %d", key->ip_proto); + if (mask->has_ip_proto) { + monitor_printf(mon, "(0x%x)", mask->ip_proto); + } + } + + if (key->has_ip_tos) { + monitor_printf(mon, " TOS %d", key->ip_tos); + if (mask->has_ip_tos) { + monitor_printf(mon, "(0x%x)", mask->ip_tos); + } + } + + if (key->has_ip_dst) { + monitor_printf(mon, " dst %s", key->ip_dst); + } + + if (action->has_goto_tbl || action->has_group_id || + action->has_new_vlan_id) { + monitor_printf(mon, " -->"); + } + + if (action->has_new_vlan_id) { + monitor_printf(mon, " apply new vlan %d", + ntohs(action->new_vlan_id)); + } + + if (action->has_group_id) { + monitor_printf(mon, " write group 0x%08x", action->group_id); + } + + if (action->has_goto_tbl) { + monitor_printf(mon, " goto tbl %d", action->goto_tbl); + } + + monitor_printf(mon, "\n"); + } + + qapi_free_RockerOfDpaFlowList(list); +} + +void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) +{ + RockerOfDpaGroupList *list, *g; + const char *name = qdict_get_str(qdict, "name"); + uint8_t type = qdict_get_try_int(qdict, "type", 9); + Error *errp = NULL; + bool set = false; + + list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, "id (decode) --> buckets\n"); + + for (g = list; g; g = g->next) { + RockerOfDpaGroup *group = g->value; + + monitor_printf(mon, "0x%08x", group->id); + + monitor_printf(mon, " (type %s", group->type == 0 ? "L2 interface" : + group->type == 1 ? "L2 rewrite" : + group->type == 2 ? "L3 unicast" : + group->type == 3 ? "L2 multicast" : + group->type == 4 ? "L2 flood" : + group->type == 5 ? "L3 interface" : + group->type == 6 ? "L3 multicast" : + group->type == 7 ? "L3 ECMP" : + group->type == 8 ? "L2 overlay" : + "unknown"); + + if (group->has_vlan_id) { + monitor_printf(mon, " vlan %d", group->vlan_id); + } + + if (group->has_pport) { + monitor_printf(mon, " pport %d", group->pport); + } + + if (group->has_index) { + monitor_printf(mon, " index %d", group->index); + } + + monitor_printf(mon, ") -->"); + + if (group->has_set_vlan_id && group->set_vlan_id) { + set = true; + monitor_printf(mon, " set vlan %d", + group->set_vlan_id & VLAN_VID_MASK); + } + + if (group->has_set_eth_src) { + if (!set) { + set = true; + monitor_printf(mon, " set"); + } + monitor_printf(mon, " src %s", group->set_eth_src); + } + + if (group->has_set_eth_dst) { + if (!set) { + set = true; + monitor_printf(mon, " set"); + } + monitor_printf(mon, " dst %s", group->set_eth_dst); + } + + set = false; + + if (group->has_ttl_check && group->ttl_check) { + monitor_printf(mon, " check TTL"); + } + + if (group->has_group_id && group->group_id) { + monitor_printf(mon, " group id 0x%08x", group->group_id); + } + + if (group->has_pop_vlan && group->pop_vlan) { + monitor_printf(mon, " pop vlan"); + } + + if (group->has_out_pport) { + monitor_printf(mon, " out pport %d", group->out_pport); + } + + if (group->has_group_ids) { + struct uint32List *id; + + monitor_printf(mon, " groups ["); + for (id = group->group_ids; id; id = id->next) { + monitor_printf(mon, "0x%08x", id->value); + if (id->next) { + monitor_printf(mon, ","); + } + } + monitor_printf(mon, "]"); + } + + monitor_printf(mon, "\n"); + } + + qapi_free_RockerOfDpaGroupList(list); +} @@ -124,5 +124,9 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str); void delvm_completion(ReadLineState *rs, int nb_args, const char *str); void loadvm_completion(ReadLineState *rs, int nb_args, const char *str); +void hmp_rocker(Monitor *mon, const QDict *qdict); +void hmp_rocker_ports(Monitor *mon, const QDict *qdict); +void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict); +void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict); #endif diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 25bc023..8a64ffb 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -152,6 +152,7 @@ static const VMStateDescription vmstate_memhp_state = { .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, + .needed = vmstate_test_use_memhp, .fields = (VMStateField[]) { VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs), VMSTATE_END_OF_LIST() @@ -175,12 +176,9 @@ const VMStateDescription vmstate_ich9_pm = { VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_memhp_state, - .needed = vmstate_test_use_memhp, - }, - VMSTATE_END_OF_LIST() + .subsections = (const VMStateDescription*[]) { + &vmstate_memhp_state, + NULL } }; diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index b730ca6..3bd1d5a 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -260,6 +260,7 @@ static const VMStateDescription vmstate_memhp_state = { .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, + .needed = vmstate_test_use_memhp, .fields = (VMStateField[]) { VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, PIIX4PMState), VMSTATE_END_OF_LIST() @@ -298,12 +299,9 @@ static const VMStateDescription vmstate_acpi = { vmstate_test_use_acpi_pci_hotplug), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_memhp_state, - .needed = vmstate_test_use_memhp, - }, - VMSTATE_END_OF_LIST() + .subsections = (const VMStateDescription*[]) { + &vmstate_memhp_state, + NULL } }; diff --git a/hw/arm/boot.c b/hw/arm/boot.c index d036624..1e7fd28 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -574,15 +574,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) struct arm_boot_info *info = container_of(n, struct arm_boot_info, load_kernel_notifier); - /* CPU objects (unlike devices) are not automatically reset on system - * reset, so we must always register a handler to do so. If we're - * actually loading a kernel, the handler is also responsible for - * arranging that we start it correctly. - */ - for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) { - qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); - } - /* Load the kernel. */ if (!info->kernel_filename || info->firmware_loaded) { @@ -783,7 +774,18 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) { + CPUState *cs; + info->load_kernel_notifier.cpu = cpu; info->load_kernel_notifier.notifier.notify = arm_load_kernel_notify; qemu_add_machine_init_done_notifier(&info->load_kernel_notifier.notifier); + + /* CPU objects (unlike devices) are not automatically reset on system + * reset, so we must always register a handler to do so. If we're + * actually loading a kernel, the handler is also responsible for + * arranging that we start it correctly. + */ + for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) { + qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); + } } diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index f921a56..ec353f7 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -334,10 +334,10 @@ static uint64_t pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri) static const ARMCPRegInfo pxa_cp_reginfo[] = { /* cp14 crm==1: perf registers */ { .name = "CPPMNC", .cp = 14, .crn = 0, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, + .access = PL1_RW, .type = ARM_CP_IO, .readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write }, { .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, + .access = PL1_RW, .type = ARM_CP_IO, .readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore }, { .name = "CPINTEN", .cp = 14, .crn = 4, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -356,11 +356,11 @@ static const ARMCPRegInfo pxa_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, /* cp14 crn==6: CLKCFG */ { .name = "CLKCFG", .cp = 14, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, + .access = PL1_RW, .type = ARM_CP_IO, .readfn = pxa2xx_clkcfg_read, .writefn = pxa2xx_clkcfg_write }, /* cp14 crn==7: PWRMODE */ { .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, + .access = PL1_RW, .type = ARM_CP_IO, .readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write }, REGINFO_SENTINEL }; @@ -457,7 +457,7 @@ typedef struct { MemoryRegion iomem; qemu_irq irq; - int enable; + uint32_t enable; SSIBus *bus; uint32_t sscr[2]; @@ -470,10 +470,39 @@ typedef struct { uint8_t ssacd; uint32_t rx_fifo[16]; - int rx_level; - int rx_start; + uint32_t rx_level; + uint32_t rx_start; } PXA2xxSSPState; +static bool pxa2xx_ssp_vmstate_validate(void *opaque, int version_id) +{ + PXA2xxSSPState *s = opaque; + + return s->rx_start < sizeof(s->rx_fifo); +} + +static const VMStateDescription vmstate_pxa2xx_ssp = { + .name = "pxa2xx-ssp", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(enable, PXA2xxSSPState), + VMSTATE_UINT32_ARRAY(sscr, PXA2xxSSPState, 2), + VMSTATE_UINT32(sspsp, PXA2xxSSPState), + VMSTATE_UINT32(ssto, PXA2xxSSPState), + VMSTATE_UINT32(ssitr, PXA2xxSSPState), + VMSTATE_UINT32(sssr, PXA2xxSSPState), + VMSTATE_UINT8(sstsa, PXA2xxSSPState), + VMSTATE_UINT8(ssrsa, PXA2xxSSPState), + VMSTATE_UINT8(ssacd, PXA2xxSSPState), + VMSTATE_UINT32(rx_level, PXA2xxSSPState), + VMSTATE_UINT32(rx_start, PXA2xxSSPState), + VMSTATE_VALIDATE("fifo is 16 bytes", pxa2xx_ssp_vmstate_validate), + VMSTATE_UINT32_ARRAY(rx_fifo, PXA2xxSSPState, 16), + VMSTATE_END_OF_LIST() + } +}; + #define SSCR0 0x00 /* SSP Control register 0 */ #define SSCR1 0x04 /* SSP Control register 1 */ #define SSSR 0x08 /* SSP Status register */ @@ -705,55 +734,20 @@ static const MemoryRegionOps pxa2xx_ssp_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void pxa2xx_ssp_save(QEMUFile *f, void *opaque) +static void pxa2xx_ssp_reset(DeviceState *d) { - PXA2xxSSPState *s = (PXA2xxSSPState *) opaque; - int i; - - qemu_put_be32(f, s->enable); + PXA2xxSSPState *s = PXA2XX_SSP(d); - qemu_put_be32s(f, &s->sscr[0]); - qemu_put_be32s(f, &s->sscr[1]); - qemu_put_be32s(f, &s->sspsp); - qemu_put_be32s(f, &s->ssto); - qemu_put_be32s(f, &s->ssitr); - qemu_put_be32s(f, &s->sssr); - qemu_put_8s(f, &s->sstsa); - qemu_put_8s(f, &s->ssrsa); - qemu_put_8s(f, &s->ssacd); - - qemu_put_byte(f, s->rx_level); - for (i = 0; i < s->rx_level; i ++) - qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 0xf]); -} - -static int pxa2xx_ssp_load(QEMUFile *f, void *opaque, int version_id) -{ - PXA2xxSSPState *s = (PXA2xxSSPState *) opaque; - int i, v; - - s->enable = qemu_get_be32(f); - - qemu_get_be32s(f, &s->sscr[0]); - qemu_get_be32s(f, &s->sscr[1]); - qemu_get_be32s(f, &s->sspsp); - qemu_get_be32s(f, &s->ssto); - qemu_get_be32s(f, &s->ssitr); - qemu_get_be32s(f, &s->sssr); - qemu_get_8s(f, &s->sstsa); - qemu_get_8s(f, &s->ssrsa); - qemu_get_8s(f, &s->ssacd); - - v = qemu_get_byte(f); - if (v < 0 || v > ARRAY_SIZE(s->rx_fifo)) { - return -EINVAL; - } - s->rx_level = v; - s->rx_start = 0; - for (i = 0; i < s->rx_level; i ++) - s->rx_fifo[i] = qemu_get_byte(f); - - return 0; + s->enable = 0; + s->sscr[0] = s->sscr[1] = 0; + s->sspsp = 0; + s->ssto = 0; + s->ssitr = 0; + s->sssr = 0; + s->sstsa = 0; + s->ssrsa = 0; + s->ssacd = 0; + s->rx_start = s->rx_level = 0; } static int pxa2xx_ssp_init(SysBusDevice *sbd) @@ -766,8 +760,6 @@ static int pxa2xx_ssp_init(SysBusDevice *sbd) memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_ssp_ops, s, "pxa2xx-ssp", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - register_savevm(dev, "pxa2xx_ssp", -1, 0, - pxa2xx_ssp_save, pxa2xx_ssp_load, s); s->bus = ssi_create_bus(dev, "ssi"); return 0; @@ -1759,24 +1751,33 @@ static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem, } /* PXA Fast Infra-red Communications Port */ +#define TYPE_PXA2XX_FIR "pxa2xx-fir" +#define PXA2XX_FIR(obj) OBJECT_CHECK(PXA2xxFIrState, (obj), TYPE_PXA2XX_FIR) + struct PXA2xxFIrState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + MemoryRegion iomem; qemu_irq irq; qemu_irq rx_dma; qemu_irq tx_dma; - int enable; + uint32_t enable; CharDriverState *chr; uint8_t control[3]; uint8_t status[2]; - int rx_len; - int rx_start; + uint32_t rx_len; + uint32_t rx_start; uint8_t rx_fifo[64]; }; -static void pxa2xx_fir_reset(PXA2xxFIrState *s) +static void pxa2xx_fir_reset(DeviceState *d) { + PXA2xxFIrState *s = PXA2XX_FIR(d); + s->control[0] = 0x00; s->control[1] = 0x00; s->control[2] = 0x00; @@ -1953,73 +1954,94 @@ static void pxa2xx_fir_event(void *opaque, int event) { } -static void pxa2xx_fir_save(QEMUFile *f, void *opaque) +static void pxa2xx_fir_instance_init(Object *obj) { - PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; - int i; + PXA2xxFIrState *s = PXA2XX_FIR(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - qemu_put_be32(f, s->enable); - - qemu_put_8s(f, &s->control[0]); - qemu_put_8s(f, &s->control[1]); - qemu_put_8s(f, &s->control[2]); - qemu_put_8s(f, &s->status[0]); - qemu_put_8s(f, &s->status[1]); - - qemu_put_byte(f, s->rx_len); - for (i = 0; i < s->rx_len; i ++) - qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 63]); + memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s, + "pxa2xx-fir", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + sysbus_init_irq(sbd, &s->rx_dma); + sysbus_init_irq(sbd, &s->tx_dma); } -static int pxa2xx_fir_load(QEMUFile *f, void *opaque, int version_id) +static void pxa2xx_fir_realize(DeviceState *dev, Error **errp) { - PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; - int i; - - s->enable = qemu_get_be32(f); + PXA2xxFIrState *s = PXA2XX_FIR(dev); - qemu_get_8s(f, &s->control[0]); - qemu_get_8s(f, &s->control[1]); - qemu_get_8s(f, &s->control[2]); - qemu_get_8s(f, &s->status[0]); - qemu_get_8s(f, &s->status[1]); + if (s->chr) { + qemu_chr_fe_claim_no_fail(s->chr); + qemu_chr_add_handlers(s->chr, pxa2xx_fir_is_empty, + pxa2xx_fir_rx, pxa2xx_fir_event, s); + } +} - s->rx_len = qemu_get_byte(f); - s->rx_start = 0; - for (i = 0; i < s->rx_len; i ++) - s->rx_fifo[i] = qemu_get_byte(f); +static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id) +{ + PXA2xxFIrState *s = opaque; - return 0; + return s->rx_start < ARRAY_SIZE(s->rx_fifo); } -static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma, - CharDriverState *chr) -{ - PXA2xxFIrState *s = (PXA2xxFIrState *) - g_malloc0(sizeof(PXA2xxFIrState)); +static const VMStateDescription pxa2xx_fir_vmsd = { + .name = "pxa2xx-fir", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(enable, PXA2xxFIrState), + VMSTATE_UINT8_ARRAY(control, PXA2xxFIrState, 3), + VMSTATE_UINT8_ARRAY(status, PXA2xxFIrState, 2), + VMSTATE_UINT32(rx_len, PXA2xxFIrState), + VMSTATE_UINT32(rx_start, PXA2xxFIrState), + VMSTATE_VALIDATE("fifo is 64 bytes", pxa2xx_fir_vmstate_validate), + VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxFIrState, 64), + VMSTATE_END_OF_LIST() + } +}; - s->irq = irq; - s->rx_dma = rx_dma; - s->tx_dma = tx_dma; - s->chr = chr; +static Property pxa2xx_fir_properties[] = { + DEFINE_PROP_CHR("chardev", PXA2xxFIrState, chr), + DEFINE_PROP_END_OF_LIST(), +}; - pxa2xx_fir_reset(s); +static void pxa2xx_fir_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); - memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s, "pxa2xx-fir", 0x1000); - memory_region_add_subregion(sysmem, base, &s->iomem); + dc->realize = pxa2xx_fir_realize; + dc->vmsd = &pxa2xx_fir_vmsd; + dc->props = pxa2xx_fir_properties; + dc->reset = pxa2xx_fir_reset; +} - if (chr) { - qemu_chr_fe_claim_no_fail(chr); - qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty, - pxa2xx_fir_rx, pxa2xx_fir_event, s); - } +static const TypeInfo pxa2xx_fir_info = { + .name = TYPE_PXA2XX_FIR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PXA2xxFIrState), + .class_init = pxa2xx_fir_class_init, + .instance_init = pxa2xx_fir_instance_init, +}; - register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save, - pxa2xx_fir_load, s); +static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem, + hwaddr base, + qemu_irq irq, qemu_irq rx_dma, + qemu_irq tx_dma, + CharDriverState *chr) +{ + DeviceState *dev; + SysBusDevice *sbd; - return s; + dev = qdev_create(NULL, TYPE_PXA2XX_FIR); + qdev_prop_set_chr(dev, "chardev", chr); + qdev_init_nofail(dev); + sbd = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(sbd, 0, base); + sysbus_connect_irq(sbd, 0, irq); + sysbus_connect_irq(sbd, 1, rx_dma); + sysbus_connect_irq(sbd, 2, tx_dma); + return PXA2XX_FIR(dev); } static void pxa2xx_reset(void *opaque, int line, int level) @@ -2306,8 +2328,11 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data) { SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); sdc->init = pxa2xx_ssp_init; + dc->reset = pxa2xx_ssp_reset; + dc->vmsd = &vmstate_pxa2xx_ssp; } static const TypeInfo pxa2xx_ssp_info = { @@ -2323,6 +2348,7 @@ static void pxa2xx_register_types(void) type_register_static(&pxa2xx_ssp_info); type_register_static(&pxa2xx_i2c_info); type_register_static(&pxa2xx_rtc_sysbus_info); + type_register_static(&pxa2xx_fir_info); } type_init(pxa2xx_register_types) diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c index 9cfc714..d41ac93 100644 --- a/hw/arm/pxa2xx_pic.c +++ b/hw/arm/pxa2xx_pic.c @@ -232,7 +232,7 @@ static void pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri, #define REGINFO_FOR_PIC_CP(NAME, CRN) \ { .name = NAME, .cp = 6, .crn = CRN, .crm = 0, .opc1 = 0, .opc2 = 0, \ - .access = PL1_RW, \ + .access = PL1_RW, .type = ARM_CP_IO, \ .readfn = pxa2xx_pic_cp_read, .writefn = pxa2xx_pic_cp_write } static const ARMCPRegInfo pxa_pic_cp_reginfo[] = { diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index a9373cc..d5a8b9c 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -84,6 +84,12 @@ static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, AML_EXCLUSIVE, uart_irq)); aml_append(dev, aml_name_decl("_CRS", crs)); + + /* The _ADR entry is used to link this device to the UART described + * in the SPCR table, i.e. SPCR.base_address.address == _ADR. + */ + aml_append(dev, aml_name_decl("_ADR", aml_int(uart_memmap->base))); + aml_append(scope, dev); } @@ -334,6 +340,38 @@ build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) } static void +build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) +{ + AcpiSerialPortConsoleRedirection *spcr; + const MemMapEntry *uart_memmap = &guest_info->memmap[VIRT_UART]; + int irq = guest_info->irqmap[VIRT_UART] + ARM_SPI_BASE; + + spcr = acpi_data_push(table_data, sizeof(*spcr)); + + spcr->interface_type = 0x3; /* ARM PL011 UART */ + + spcr->base_address.space_id = AML_SYSTEM_MEMORY; + spcr->base_address.bit_width = 8; + spcr->base_address.bit_offset = 0; + spcr->base_address.access_width = 1; + spcr->base_address.address = cpu_to_le64(uart_memmap->base); + + spcr->interrupt_types = (1 << 3); /* Bit[3] ARMH GIC interrupt */ + spcr->gsi = cpu_to_le32(irq); /* Global System Interrupt */ + + spcr->baud = 3; /* Baud Rate: 3 = 9600 */ + spcr->parity = 0; /* No Parity */ + spcr->stopbits = 1; /* 1 Stop bit */ + spcr->flowctrl = (1 << 1); /* Bit[1] = RTS/CTS hardware flow control */ + spcr->term_type = 0; /* Terminal Type: 0 = VT100 */ + + spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */ + spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */ + + build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2); +} + +static void build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) { AcpiTableMcfg *mcfg; @@ -514,7 +552,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) dsdt = tables_blob->len; build_dsdt(tables_blob, tables->linker, guest_info); - /* FADT MADT GTDT pointed to by RSDT */ + /* FADT MADT GTDT SPCR pointed to by RSDT */ acpi_add_table(table_offsets, tables_blob); build_fadt(tables_blob, tables->linker, dsdt); @@ -527,6 +565,9 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_mcfg(tables_blob, tables->linker, guest_info); + acpi_add_table(table_offsets, tables_blob); + build_spcr(tables_blob, tables->linker, guest_info); + /* RSDT is pointed to by RSDP */ rsdt = tables_blob->len; build_rsdt(tables_blob, tables->linker, table_offsets); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 1b1cc71..f1e85c8 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -145,6 +145,11 @@ static VirtBoardInfo machines[] = { .irqmap = a15irqmap, }, { + .cpu_model = "cortex-a53", + .memmap = a15memmap, + .irqmap = a15irqmap, + }, + { .cpu_model = "cortex-a57", .memmap = a15memmap, .irqmap = a15irqmap, @@ -306,7 +311,7 @@ static void fdt_add_cpu_nodes(const VirtBoardInfo *vbi) "enable-method", "psci"); } - qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", cpu); + qemu_fdt_setprop_cell(vbi->fdt, nodename, "reg", armcpu->mp_affinity); g_free(nodename); } } diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 6e79459..5e1b67e 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -699,6 +699,7 @@ static const VMStateDescription vmstate_fdrive_media_changed = { .name = "fdrive/media_changed", .version_id = 1, .minimum_version_id = 1, + .needed = fdrive_media_changed_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(media_changed, FDrive), VMSTATE_END_OF_LIST() @@ -716,6 +717,7 @@ static const VMStateDescription vmstate_fdrive_media_rate = { .name = "fdrive/media_rate", .version_id = 1, .minimum_version_id = 1, + .needed = fdrive_media_rate_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(media_rate, FDrive), VMSTATE_END_OF_LIST() @@ -733,6 +735,7 @@ static const VMStateDescription vmstate_fdrive_perpendicular = { .name = "fdrive/perpendicular", .version_id = 1, .minimum_version_id = 1, + .needed = fdrive_perpendicular_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(perpendicular, FDrive), VMSTATE_END_OF_LIST() @@ -756,19 +759,11 @@ static const VMStateDescription vmstate_fdrive = { VMSTATE_UINT8(sect, FDrive), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_fdrive_media_changed, - .needed = &fdrive_media_changed_needed, - } , { - .vmsd = &vmstate_fdrive_media_rate, - .needed = &fdrive_media_rate_needed, - } , { - .vmsd = &vmstate_fdrive_perpendicular, - .needed = &fdrive_perpendicular_needed, - } , { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_fdrive_media_changed, + &vmstate_fdrive_media_rate, + &vmstate_fdrive_perpendicular, + NULL } }; @@ -833,6 +828,7 @@ static const VMStateDescription vmstate_fdc_reset_sensei = { .name = "fdc/reset_sensei", .version_id = 1, .minimum_version_id = 1, + .needed = fdc_reset_sensei_needed, .fields = (VMStateField[]) { VMSTATE_INT32(reset_sensei, FDCtrl), VMSTATE_END_OF_LIST() @@ -850,6 +846,7 @@ static const VMStateDescription vmstate_fdc_result_timer = { .name = "fdc/result_timer", .version_id = 1, .minimum_version_id = 1, + .needed = fdc_result_timer_needed, .fields = (VMStateField[]) { VMSTATE_TIMER_PTR(result_timer, FDCtrl), VMSTATE_END_OF_LIST() @@ -867,6 +864,7 @@ static const VMStateDescription vmstate_fdc_phase = { .name = "fdc/phase", .version_id = 1, .minimum_version_id = 1, + .needed = fdc_phase_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(phase, FDCtrl), VMSTATE_END_OF_LIST() @@ -911,19 +909,11 @@ static const VMStateDescription vmstate_fdc = { vmstate_fdrive, FDrive), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_fdc_reset_sensei, - .needed = fdc_reset_sensei_needed, - } , { - .vmsd = &vmstate_fdc_result_timer, - .needed = fdc_result_timer_needed, - } , { - .vmsd = &vmstate_fdc_phase, - .needed = fdc_phase_needed, - } , { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_fdc_reset_sensei, + &vmstate_fdc_result_timer, + &vmstate_fdc_phase, + NULL } }; diff --git a/hw/char/serial.c b/hw/char/serial.c index 55011cf..513d73c 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -662,6 +662,7 @@ static const VMStateDescription vmstate_serial_thr_ipending = { .name = "serial/thr_ipending", .version_id = 1, .minimum_version_id = 1, + .needed = serial_thr_ipending_needed, .fields = (VMStateField[]) { VMSTATE_INT32(thr_ipending, SerialState), VMSTATE_END_OF_LIST() @@ -678,6 +679,7 @@ static const VMStateDescription vmstate_serial_tsr = { .name = "serial/tsr", .version_id = 1, .minimum_version_id = 1, + .needed = serial_tsr_needed, .fields = (VMStateField[]) { VMSTATE_INT32(tsr_retry, SerialState), VMSTATE_UINT8(thr, SerialState), @@ -697,6 +699,7 @@ static const VMStateDescription vmstate_serial_recv_fifo = { .name = "serial/recv_fifo", .version_id = 1, .minimum_version_id = 1, + .needed = serial_recv_fifo_needed, .fields = (VMStateField[]) { VMSTATE_STRUCT(recv_fifo, SerialState, 1, vmstate_fifo8, Fifo8), VMSTATE_END_OF_LIST() @@ -713,6 +716,7 @@ static const VMStateDescription vmstate_serial_xmit_fifo = { .name = "serial/xmit_fifo", .version_id = 1, .minimum_version_id = 1, + .needed = serial_xmit_fifo_needed, .fields = (VMStateField[]) { VMSTATE_STRUCT(xmit_fifo, SerialState, 1, vmstate_fifo8, Fifo8), VMSTATE_END_OF_LIST() @@ -729,6 +733,7 @@ static const VMStateDescription vmstate_serial_fifo_timeout_timer = { .name = "serial/fifo_timeout_timer", .version_id = 1, .minimum_version_id = 1, + .needed = serial_fifo_timeout_timer_needed, .fields = (VMStateField[]) { VMSTATE_TIMER_PTR(fifo_timeout_timer, SerialState), VMSTATE_END_OF_LIST() @@ -745,6 +750,7 @@ static const VMStateDescription vmstate_serial_timeout_ipending = { .name = "serial/timeout_ipending", .version_id = 1, .minimum_version_id = 1, + .needed = serial_timeout_ipending_needed, .fields = (VMStateField[]) { VMSTATE_INT32(timeout_ipending, SerialState), VMSTATE_END_OF_LIST() @@ -760,6 +766,7 @@ static bool serial_poll_needed(void *opaque) static const VMStateDescription vmstate_serial_poll = { .name = "serial/poll", .version_id = 1, + .needed = serial_poll_needed, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_INT32(poll_msl, SerialState), @@ -788,31 +795,15 @@ const VMStateDescription vmstate_serial = { VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_serial_thr_ipending, - .needed = &serial_thr_ipending_needed, - } , { - .vmsd = &vmstate_serial_tsr, - .needed = &serial_tsr_needed, - } , { - .vmsd = &vmstate_serial_recv_fifo, - .needed = &serial_recv_fifo_needed, - } , { - .vmsd = &vmstate_serial_xmit_fifo, - .needed = &serial_xmit_fifo_needed, - } , { - .vmsd = &vmstate_serial_fifo_timeout_timer, - .needed = &serial_fifo_timeout_timer_needed, - } , { - .vmsd = &vmstate_serial_timeout_ipending, - .needed = &serial_timeout_ipending_needed, - } , { - .vmsd = &vmstate_serial_poll, - .needed = &serial_poll_needed, - } , { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_serial_thr_ipending, + &vmstate_serial_tsr, + &vmstate_serial_recv_fifo, + &vmstate_serial_xmit_fifo, + &vmstate_serial_fifo_timeout_timer, + &vmstate_serial_timeout_ipending, + &vmstate_serial_poll, + NULL } }; diff --git a/hw/core/nmi.c b/hw/core/nmi.c index 3dff020..5260d6c 100644 --- a/hw/core/nmi.c +++ b/hw/core/nmi.c @@ -21,6 +21,7 @@ #include "hw/nmi.h" #include "qapi/qmp/qerror.h" +#include "monitor/monitor.h" struct do_nmi_s { int cpu_index; @@ -70,6 +71,25 @@ void nmi_monitor_handle(int cpu_index, Error **errp) } } +void inject_nmi(void) +{ +#if defined(TARGET_I386) + CPUState *cs; + + CPU_FOREACH(cs) { + X86CPU *cpu = X86_CPU(cs); + + if (!cpu->apic_state) { + cpu_interrupt(cs, CPU_INTERRUPT_NMI); + } else { + apic_deliver_nmi(cpu->apic_state); + } + } +#else + nmi_monitor_handle(0, NULL); +#endif +} + static const TypeInfo nmi_info = { .name = TYPE_NMI, .parent = TYPE_INTERFACE, diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 72b3a1d..603ef50 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -337,7 +337,7 @@ static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data) if (swap_ctl & FIMD_WINCON_SWAP_BITS) { res = 0; for (i = 0; i < 64; i++) { - if (x & (1ULL << (64 - i))) { + if (x & (1ULL << (63 - i))) { res |= (1ULL << i); } } diff --git a/hw/display/qxl.c b/hw/display/qxl.c index b220e2d..722146e 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2220,6 +2220,7 @@ static VMStateDescription qxl_vmstate_monitors_config = { .name = "qxl/monitors-config", .version_id = 1, .minimum_version_id = 1, + .needed = qxl_monitors_config_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice), VMSTATE_END_OF_LIST() @@ -2253,13 +2254,9 @@ static VMStateDescription qxl_vmstate = { VMSTATE_UINT64(guest_cursor, PCIQXLDevice), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &qxl_vmstate_monitors_config, - .needed = qxl_monitors_config_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &qxl_vmstate_monitors_config, + NULL } }; diff --git a/hw/display/vga.c b/hw/display/vga.c index d1d296c..b35d523 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -2035,6 +2035,7 @@ static const VMStateDescription vmstate_vga_endian = { .name = "vga.endian", .version_id = 1, .minimum_version_id = 1, + .needed = vga_endian_state_needed, .fields = (VMStateField[]) { VMSTATE_BOOL(big_endian_fb, VGACommonState), VMSTATE_END_OF_LIST() @@ -2078,13 +2079,9 @@ const VMStateDescription vmstate_vga_common = { VMSTATE_UINT32(vbe_bank_mask, VGACommonState), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_vga_endian, - .needed = vga_endian_state_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_vga_endian, + NULL } }; diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index af26632..3efa6de 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -1,7 +1,7 @@ /* * QEMU JAZZ RC4030 chipset * - * Copyright (c) 2007-2009 Herve Poussineau + * Copyright (c) 2007-2013 Hervé 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 @@ -24,29 +24,16 @@ #include "hw/hw.h" #include "hw/mips/mips.h" +#include "hw/sysbus.h" #include "qemu/timer.h" - -/********************************************************/ -/* debug rc4030 */ - -//#define DEBUG_RC4030 -//#define DEBUG_RC4030_DMA - -#ifdef DEBUG_RC4030 -#define DPRINTF(fmt, ...) \ -do { printf("rc4030: " fmt , ## __VA_ARGS__); } while (0) -static const char* irq_names[] = { "parallel", "floppy", "sound", "video", - "network", "scsi", "keyboard", "mouse", "serial0", "serial1" }; -#else -#define DPRINTF(fmt, ...) -#endif - -#define RC4030_ERROR(fmt, ...) \ -do { fprintf(stderr, "rc4030 ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) +#include "exec/address-spaces.h" +#include "trace.h" /********************************************************/ /* rc4030 emulation */ +#define MAX_TL_ENTRIES 512 + typedef struct dma_pagetable_entry { int32_t frame; int32_t owner; @@ -63,8 +50,14 @@ typedef struct dma_pagetable_entry { #define DMA_FLAG_MEM_INTR 0x0200 #define DMA_FLAG_ADDR_INTR 0x0400 +#define TYPE_RC4030 "rc4030" +#define RC4030(obj) \ + OBJECT_CHECK(rc4030State, (obj), TYPE_RC4030) + typedef struct rc4030State { + SysBusDevice parent; + uint32_t config; /* 0x0000: RC4030 config register */ uint32_t revision; /* 0x0008: RC4030 Revision register */ uint32_t invalid_address_register; /* 0x0010: Invalid Address register */ @@ -83,7 +76,7 @@ typedef struct rc4030State uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */ uint32_t nmi_interrupt; /* 0x0200: interrupt source */ - uint32_t offset210; + uint32_t memory_refresh_rate; /* 0x0210: memory refresh rate */ uint32_t nvram_protect; /* 0x0220: NV ram protect register */ uint32_t rem_speed[16]; uint32_t imr_jazz; /* Local bus int enable mask */ @@ -96,6 +89,16 @@ typedef struct rc4030State qemu_irq timer_irq; qemu_irq jazz_bus_irq; + /* biggest translation table */ + MemoryRegion dma_tt; + /* translation table memory region alias, added to system RAM */ + MemoryRegion dma_tt_alias; + /* whole DMA memory region, root of DMA address space */ + MemoryRegion dma_mr; + /* translation table entry aliases, added to DMA memory region */ + MemoryRegion dma_mrs[MAX_TL_ENTRIES]; + AddressSpace dma_as; + MemoryRegion iomem_chipset; MemoryRegion iomem_jazzio; } rc4030State; @@ -112,7 +115,7 @@ static void set_next_tick(rc4030State *s) } /* called for accesses to rc4030 */ -static uint32_t rc4030_readl(void *opaque, hwaddr addr) +static uint64_t rc4030_read(void *opaque, hwaddr addr, unsigned int size) { rc4030State *s = opaque; uint32_t val; @@ -220,9 +223,9 @@ static uint32_t rc4030_readl(void *opaque, hwaddr addr) case 0x0208: val = 0; break; - /* Offset 0x0210 */ + /* Memory refresh rate */ case 0x0210: - val = s->offset210; + val = s->memory_refresh_rate; break; /* NV ram protect register */ case 0x0220: @@ -238,39 +241,117 @@ static uint32_t rc4030_readl(void *opaque, hwaddr addr) val = 7; /* FIXME: should be read from EISA controller */ break; default: - RC4030_ERROR("invalid read [" TARGET_FMT_plx "]\n", addr); + qemu_log_mask(LOG_GUEST_ERROR, + "rc4030: invalid read at 0x%x", (int)addr); val = 0; break; } if ((addr & ~3) != 0x230) { - DPRINTF("read 0x%02x at " TARGET_FMT_plx "\n", val, addr); + trace_rc4030_read(addr, val); } return val; } -static uint32_t rc4030_readw(void *opaque, hwaddr addr) +static void rc4030_dma_as_update_one(rc4030State *s, int index, uint32_t frame) { - uint32_t v = rc4030_readl(opaque, addr & ~0x3); - if (addr & 0x2) - return v >> 16; - else - return v & 0xffff; + if (index < MAX_TL_ENTRIES) { + memory_region_set_enabled(&s->dma_mrs[index], false); + } + + if (!frame) { + return; + } + + if (index >= MAX_TL_ENTRIES) { + qemu_log_mask(LOG_UNIMP, + "rc4030: trying to use too high " + "translation table entry %d (max allowed=%d)", + index, MAX_TL_ENTRIES); + return; + } + memory_region_set_alias_offset(&s->dma_mrs[index], frame); + memory_region_set_enabled(&s->dma_mrs[index], true); } -static uint32_t rc4030_readb(void *opaque, hwaddr addr) +static void rc4030_dma_tt_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) { - uint32_t v = rc4030_readl(opaque, addr & ~0x3); - return (v >> (8 * (addr & 0x3))) & 0xff; + rc4030State *s = opaque; + + /* write memory */ + memcpy(memory_region_get_ram_ptr(&s->dma_tt) + addr, &data, size); + + /* update dma address space (only if frame field has been written) */ + if (addr % sizeof(dma_pagetable_entry) == 0) { + int index = addr / sizeof(dma_pagetable_entry); + memory_region_transaction_begin(); + rc4030_dma_as_update_one(s, index, (uint32_t)data); + memory_region_transaction_commit(); + } } -static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val) +static const MemoryRegionOps rc4030_dma_tt_ops = { + .write = rc4030_dma_tt_write, + .impl.min_access_size = 4, + .impl.max_access_size = 4, +}; + +static void rc4030_dma_tt_update(rc4030State *s, uint32_t new_tl_base, + uint32_t new_tl_limit) +{ + int entries, i; + dma_pagetable_entry *dma_tl_contents; + + if (s->dma_tl_limit) { + /* write old dma tl table to physical memory */ + memory_region_del_subregion(get_system_memory(), &s->dma_tt_alias); + cpu_physical_memory_write(s->dma_tl_limit & 0x7fffffff, + memory_region_get_ram_ptr(&s->dma_tt), + memory_region_size(&s->dma_tt_alias)); + } + object_unparent(OBJECT(&s->dma_tt_alias)); + + s->dma_tl_base = new_tl_base; + s->dma_tl_limit = new_tl_limit; + new_tl_base &= 0x7fffffff; + + if (s->dma_tl_limit) { + uint64_t dma_tt_size; + if (s->dma_tl_limit <= memory_region_size(&s->dma_tt)) { + dma_tt_size = s->dma_tl_limit; + } else { + dma_tt_size = memory_region_size(&s->dma_tt); + } + memory_region_init_alias(&s->dma_tt_alias, OBJECT(s), + "dma-table-alias", + &s->dma_tt, 0, dma_tt_size); + dma_tl_contents = memory_region_get_ram_ptr(&s->dma_tt); + cpu_physical_memory_read(new_tl_base, dma_tl_contents, dma_tt_size); + + memory_region_transaction_begin(); + entries = dma_tt_size / sizeof(dma_pagetable_entry); + for (i = 0; i < entries; i++) { + rc4030_dma_as_update_one(s, i, dma_tl_contents[i].frame); + } + memory_region_add_subregion(get_system_memory(), new_tl_base, + &s->dma_tt_alias); + memory_region_transaction_commit(); + } else { + memory_region_init(&s->dma_tt_alias, OBJECT(s), + "dma-table-alias", 0); + } +} + +static void rc4030_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) { rc4030State *s = opaque; + uint32_t val = data; addr &= 0x3fff; - DPRINTF("write 0x%02x at " TARGET_FMT_plx "\n", val, addr); + trace_rc4030_write(addr, val); switch (addr & ~0x3) { /* Global config register */ @@ -279,11 +360,11 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val) break; /* DMA transl. table base */ case 0x0018: - s->dma_tl_base = val; + rc4030_dma_tt_update(s, val, s->dma_tl_limit); break; /* DMA transl. table limit */ case 0x0020: - s->dma_tl_limit = val; + rc4030_dma_tt_update(s, s->dma_tl_base, val); break; /* DMA transl. table invalidated */ case 0x0028: @@ -371,9 +452,9 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val) s->dma_regs[entry][idx] = val; } break; - /* Offset 0x0210 */ + /* Memory refresh rate */ case 0x0210: - s->offset210 = val; + s->memory_refresh_rate = val; break; /* Interval timer reload */ case 0x0228: @@ -385,48 +466,18 @@ static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val) case 0x0238: break; default: - RC4030_ERROR("invalid write of 0x%02x at [" TARGET_FMT_plx "]\n", val, addr); - break; - } -} - -static void rc4030_writew(void *opaque, hwaddr addr, uint32_t val) -{ - uint32_t old_val = rc4030_readl(opaque, addr & ~0x3); - - if (addr & 0x2) - val = (val << 16) | (old_val & 0x0000ffff); - else - val = val | (old_val & 0xffff0000); - rc4030_writel(opaque, addr & ~0x3, val); -} - -static void rc4030_writeb(void *opaque, hwaddr addr, uint32_t val) -{ - uint32_t old_val = rc4030_readl(opaque, addr & ~0x3); - - switch (addr & 3) { - case 0: - val = val | (old_val & 0xffffff00); - break; - case 1: - val = (val << 8) | (old_val & 0xffff00ff); - break; - case 2: - val = (val << 16) | (old_val & 0xff00ffff); - break; - case 3: - val = (val << 24) | (old_val & 0x00ffffff); + qemu_log_mask(LOG_GUEST_ERROR, + "rc4030: invalid write of 0x%02x at 0x%x", + val, (int)addr); break; } - rc4030_writel(opaque, addr & ~0x3, val); } static const MemoryRegionOps rc4030_ops = { - .old_mmio = { - .read = { rc4030_readb, rc4030_readw, rc4030_readl, }, - .write = { rc4030_writeb, rc4030_writew, rc4030_writel, }, - }, + .read = rc4030_read, + .write = rc4030_write, + .impl.min_access_size = 4, + .impl.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -436,22 +487,6 @@ static void update_jazz_irq(rc4030State *s) pending = s->isr_jazz & s->imr_jazz; -#ifdef DEBUG_RC4030 - if (s->isr_jazz != 0) { - uint32_t irq = 0; - DPRINTF("pending irqs:"); - for (irq = 0; irq < ARRAY_SIZE(irq_names); irq++) { - if (s->isr_jazz & (1 << irq)) { - printf(" %s", irq_names[irq]); - if (!(s->imr_jazz & (1 << irq))) { - printf("(ignored)"); - } - } - } - printf("\n"); - } -#endif - if (pending != 0) qemu_irq_raise(s->jazz_bus_irq); else @@ -479,7 +514,7 @@ static void rc4030_periodic_timer(void *opaque) qemu_irq_raise(s->timer_irq); } -static uint32_t jazzio_readw(void *opaque, hwaddr addr) +static uint64_t jazzio_read(void *opaque, hwaddr addr, unsigned int size) { rc4030State *s = opaque; uint32_t val; @@ -494,7 +529,6 @@ static uint32_t jazzio_readw(void *opaque, hwaddr addr) irq = 0; while (pending) { if (pending & 1) { - DPRINTF("returning irq %s\n", irq_names[irq]); val = (irq + 1) << 2; break; } @@ -508,36 +542,25 @@ static uint32_t jazzio_readw(void *opaque, hwaddr addr) val = s->imr_jazz; break; default: - RC4030_ERROR("(jazz io controller) invalid read [" TARGET_FMT_plx "]\n", addr); + qemu_log_mask(LOG_GUEST_ERROR, + "rc4030/jazzio: invalid read at 0x%x", (int)addr); val = 0; + break; } - DPRINTF("(jazz io controller) read 0x%04x at " TARGET_FMT_plx "\n", val, addr); + trace_jazzio_read(addr, val); return val; } -static uint32_t jazzio_readb(void *opaque, hwaddr addr) -{ - uint32_t v; - v = jazzio_readw(opaque, addr & ~0x1); - return (v >> (8 * (addr & 0x1))) & 0xff; -} - -static uint32_t jazzio_readl(void *opaque, hwaddr addr) -{ - uint32_t v; - v = jazzio_readw(opaque, addr); - v |= jazzio_readw(opaque, addr + 2) << 16; - return v; -} - -static void jazzio_writew(void *opaque, hwaddr addr, uint32_t val) +static void jazzio_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) { rc4030State *s = opaque; + uint32_t val = data; addr &= 0xfff; - DPRINTF("(jazz io controller) write 0x%04x at " TARGET_FMT_plx "\n", val, addr); + trace_jazzio_write(addr, val); switch (addr) { /* Local bus int enable mask */ @@ -546,43 +569,24 @@ static void jazzio_writew(void *opaque, hwaddr addr, uint32_t val) update_jazz_irq(s); break; default: - RC4030_ERROR("(jazz io controller) invalid write of 0x%04x at [" TARGET_FMT_plx "]\n", val, addr); + qemu_log_mask(LOG_GUEST_ERROR, + "rc4030/jazzio: invalid write of 0x%02x at 0x%x", + val, (int)addr); break; } } -static void jazzio_writeb(void *opaque, hwaddr addr, uint32_t val) -{ - uint32_t old_val = jazzio_readw(opaque, addr & ~0x1); - - switch (addr & 1) { - case 0: - val = val | (old_val & 0xff00); - break; - case 1: - val = (val << 8) | (old_val & 0x00ff); - break; - } - jazzio_writew(opaque, addr & ~0x1, val); -} - -static void jazzio_writel(void *opaque, hwaddr addr, uint32_t val) -{ - jazzio_writew(opaque, addr, val & 0xffff); - jazzio_writew(opaque, addr + 2, (val >> 16) & 0xffff); -} - static const MemoryRegionOps jazzio_ops = { - .old_mmio = { - .read = { jazzio_readb, jazzio_readw, jazzio_readl, }, - .write = { jazzio_writeb, jazzio_writew, jazzio_writel, }, - }, + .read = jazzio_read, + .write = jazzio_write, + .impl.min_access_size = 2, + .impl.max_access_size = 2, .endianness = DEVICE_NATIVE_ENDIAN, }; -static void rc4030_reset(void *opaque) +static void rc4030_reset(DeviceState *dev) { - rc4030State *s = opaque; + rc4030State *s = RC4030(dev); int i; s->config = 0x410; /* some boards seem to accept 0x104 too */ @@ -590,14 +594,14 @@ static void rc4030_reset(void *opaque) s->invalid_address_register = 0; memset(s->dma_regs, 0, sizeof(s->dma_regs)); - s->dma_tl_base = s->dma_tl_limit = 0; + rc4030_dma_tt_update(s, 0, 0); s->remote_failed_address = s->memory_failed_address = 0; s->cache_maint = 0; s->cache_ptag = s->cache_ltag = 0; s->cache_bmask = 0; - s->offset210 = 0x18186; + s->memory_refresh_rate = 0x18186; s->nvram_protect = 7; for (i = 0; i < 15; i++) s->rem_speed[i] = 7; @@ -631,7 +635,7 @@ static int rc4030_load(QEMUFile *f, void *opaque, int version_id) s->cache_ptag = qemu_get_be32(f); s->cache_ltag = qemu_get_be32(f); s->cache_bmask = qemu_get_be32(f); - s->offset210 = qemu_get_be32(f); + s->memory_refresh_rate = qemu_get_be32(f); s->nvram_protect = qemu_get_be32(f); for (i = 0; i < 15; i++) s->rem_speed[i] = qemu_get_be32(f); @@ -663,7 +667,7 @@ static void rc4030_save(QEMUFile *f, void *opaque) qemu_put_be32(f, s->cache_ptag); qemu_put_be32(f, s->cache_ltag); qemu_put_be32(f, s->cache_bmask); - qemu_put_be32(f, s->offset210); + qemu_put_be32(f, s->memory_refresh_rate); qemu_put_be32(f, s->nvram_protect); for (i = 0; i < 15; i++) qemu_put_be32(f, s->rem_speed[i]); @@ -672,44 +676,6 @@ static void rc4030_save(QEMUFile *f, void *opaque) qemu_put_be32(f, s->itr); } -void rc4030_dma_memory_rw(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write) -{ - rc4030State *s = opaque; - hwaddr entry_addr; - hwaddr phys_addr; - dma_pagetable_entry entry; - int index; - int ncpy, i; - - i = 0; - for (;;) { - if (i == len) { - break; - } - - ncpy = DMA_PAGESIZE - (addr & (DMA_PAGESIZE - 1)); - if (ncpy > len - i) - ncpy = len - i; - - /* Get DMA translation table entry */ - index = addr / DMA_PAGESIZE; - if (index >= s->dma_tl_limit / sizeof(dma_pagetable_entry)) { - break; - } - entry_addr = s->dma_tl_base + index * sizeof(dma_pagetable_entry); - /* XXX: not sure. should we really use only lowest bits? */ - entry_addr &= 0x7fffffff; - cpu_physical_memory_read(entry_addr, &entry, sizeof(entry)); - - /* Read/write data at right place */ - phys_addr = entry.frame + (addr & (DMA_PAGESIZE - 1)); - cpu_physical_memory_rw(phys_addr, &buf[i], ncpy, is_write); - - i += ncpy; - addr += ncpy; - } -} - static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write) { rc4030State *s = opaque; @@ -733,32 +699,11 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri dma_addr = s->dma_regs[n][DMA_REG_ADDRESS]; /* Read/write data at right place */ - rc4030_dma_memory_rw(opaque, dma_addr, buf, len, is_write); + address_space_rw(&s->dma_as, dma_addr, MEMTXATTRS_UNSPECIFIED, + buf, len, is_write); s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR; s->dma_regs[n][DMA_REG_COUNT] -= len; - -#ifdef DEBUG_RC4030_DMA - { - int i, j; - printf("rc4030 dma: Copying %d bytes %s host %p\n", - len, is_write ? "from" : "to", buf); - for (i = 0; i < len; i += 16) { - int n = 16; - if (n > len - i) { - n = len - i; - } - for (j = 0; j < n; j++) - printf("%02x ", buf[i + j]); - while (j++ < 16) - printf(" "); - printf("| "); - for (j = 0; j < n; j++) - printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.'); - printf("\n"); - } - } -#endif } struct rc4030DMAState { @@ -795,31 +740,102 @@ static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n) return s; } -void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus, - qemu_irq **irqs, rc4030_dma **dmas, - MemoryRegion *sysmem) +static void rc4030_initfn(Object *obj) { - rc4030State *s; + DeviceState *dev = DEVICE(obj); + rc4030State *s = RC4030(obj); + SysBusDevice *sysbus = SYS_BUS_DEVICE(obj); - s = g_malloc0(sizeof(rc4030State)); + qdev_init_gpio_in(dev, rc4030_irq_jazz_request, 16); - *irqs = qemu_allocate_irqs(rc4030_irq_jazz_request, s, 16); - *dmas = rc4030_allocate_dmas(s, 4); + sysbus_init_irq(sysbus, &s->timer_irq); + sysbus_init_irq(sysbus, &s->jazz_bus_irq); - s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, rc4030_periodic_timer, s); - s->timer_irq = timer; - s->jazz_bus_irq = jazz_bus; - - qemu_register_reset(rc4030_reset, s); register_savevm(NULL, "rc4030", 0, 2, rc4030_save, rc4030_load, s); - rc4030_reset(s); + + sysbus_init_mmio(sysbus, &s->iomem_chipset); + sysbus_init_mmio(sysbus, &s->iomem_jazzio); +} + +static void rc4030_realize(DeviceState *dev, Error **errp) +{ + rc4030State *s = RC4030(dev); + Object *o = OBJECT(dev); + int i; + + s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + rc4030_periodic_timer, s); memory_region_init_io(&s->iomem_chipset, NULL, &rc4030_ops, s, "rc4030.chipset", 0x300); - memory_region_add_subregion(sysmem, 0x80000000, &s->iomem_chipset); memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s, "rc4030.jazzio", 0x00001000); - memory_region_add_subregion(sysmem, 0xf0000000, &s->iomem_jazzio); - return s; + memory_region_init_rom_device(&s->dma_tt, o, + &rc4030_dma_tt_ops, s, "dma-table", + MAX_TL_ENTRIES * sizeof(dma_pagetable_entry), + NULL); + memory_region_init(&s->dma_tt_alias, o, "dma-table-alias", 0); + memory_region_init(&s->dma_mr, o, "dma", INT32_MAX); + for (i = 0; i < MAX_TL_ENTRIES; ++i) { + memory_region_init_alias(&s->dma_mrs[i], o, "dma-alias", + get_system_memory(), 0, DMA_PAGESIZE); + memory_region_set_enabled(&s->dma_mrs[i], false); + memory_region_add_subregion(&s->dma_mr, i * DMA_PAGESIZE, + &s->dma_mrs[i]); + } + address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma"); +} + +static void rc4030_unrealize(DeviceState *dev, Error **errp) +{ + rc4030State *s = RC4030(dev); + int i; + + timer_free(s->periodic_timer); + + address_space_destroy(&s->dma_as); + object_unparent(OBJECT(&s->dma_tt)); + object_unparent(OBJECT(&s->dma_tt_alias)); + object_unparent(OBJECT(&s->dma_mr)); + for (i = 0; i < MAX_TL_ENTRIES; ++i) { + memory_region_del_subregion(&s->dma_mr, &s->dma_mrs[i]); + object_unparent(OBJECT(&s->dma_mrs[i])); + } +} + +static void rc4030_class_init(ObjectClass *klass, void *class_data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = rc4030_realize; + dc->unrealize = rc4030_unrealize; + dc->reset = rc4030_reset; +} + +static const TypeInfo rc4030_info = { + .name = TYPE_RC4030, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(rc4030State), + .instance_init = rc4030_initfn, + .class_init = rc4030_class_init, +}; + +static void rc4030_register_types(void) +{ + type_register_static(&rc4030_info); +} + +type_init(rc4030_register_types) + +DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr) +{ + DeviceState *dev; + + dev = qdev_create(NULL, TYPE_RC4030); + qdev_init_nofail(dev); + + *dmas = rc4030_allocate_dmas(dev, 4); + *dma_mr = &RC4030(dev)->dma_mr; + return dev; } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 5253e6d..e142f75 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -52,6 +52,7 @@ #ifdef CONFIG_XEN # include <xen/hvm/hvm_info_table.h> #endif +#include "migration/migration.h" #define MAX_IDE_BUS 2 @@ -305,6 +306,7 @@ static void pc_init1(MachineState *machine) static void pc_compat_2_3(MachineState *machine) { + savevm_skip_section_footers(); } static void pc_compat_2_2(MachineState *machine) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 110dfb7..b68263d 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -45,6 +45,7 @@ #include "hw/usb.h" #include "hw/cpu/icc_bus.h" #include "qemu/error-report.h" +#include "migration/migration.h" /* ICH9 AHCI has 6 ports */ #define MAX_SATA_PORTS 6 @@ -289,6 +290,7 @@ static void pc_q35_init(MachineState *machine) static void pc_compat_2_3(MachineState *machine) { + savevm_skip_section_footers(); } static void pc_compat_2_2(MachineState *machine) diff --git a/hw/ide/core.c b/hw/ide/core.c index fcb9080..1efd98a 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2561,6 +2561,7 @@ static const VMStateDescription vmstate_ide_atapi_gesn_state = { .name ="ide_drive/atapi/gesn_state", .version_id = 1, .minimum_version_id = 1, + .needed = ide_atapi_gesn_needed, .fields = (VMStateField[]) { VMSTATE_BOOL(events.new_media, IDEState), VMSTATE_BOOL(events.eject_request, IDEState), @@ -2572,6 +2573,7 @@ static const VMStateDescription vmstate_ide_tray_state = { .name = "ide_drive/tray_state", .version_id = 1, .minimum_version_id = 1, + .needed = ide_tray_state_needed, .fields = (VMStateField[]) { VMSTATE_BOOL(tray_open, IDEState), VMSTATE_BOOL(tray_locked, IDEState), @@ -2585,6 +2587,7 @@ static const VMStateDescription vmstate_ide_drive_pio_state = { .minimum_version_id = 1, .pre_save = ide_drive_pio_pre_save, .post_load = ide_drive_pio_post_load, + .needed = ide_drive_pio_state_needed, .fields = (VMStateField[]) { VMSTATE_INT32(req_nb_sectors, IDEState), VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1, @@ -2626,19 +2629,11 @@ const VMStateDescription vmstate_ide_drive = { VMSTATE_UINT8_V(cdrom_changed, IDEState, 3), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_ide_drive_pio_state, - .needed = ide_drive_pio_state_needed, - }, { - .vmsd = &vmstate_ide_tray_state, - .needed = ide_tray_state_needed, - }, { - .vmsd = &vmstate_ide_atapi_gesn_state, - .needed = ide_atapi_gesn_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_ide_drive_pio_state, + &vmstate_ide_tray_state, + &vmstate_ide_atapi_gesn_state, + NULL } }; @@ -2646,6 +2641,7 @@ static const VMStateDescription vmstate_ide_error_status = { .name ="ide_bus/error", .version_id = 2, .minimum_version_id = 1, + .needed = ide_error_needed, .fields = (VMStateField[]) { VMSTATE_INT32(error_status, IDEBus), VMSTATE_INT64_V(retry_sector_num, IDEBus, 2), @@ -2664,13 +2660,9 @@ const VMStateDescription vmstate_ide_bus = { VMSTATE_UINT8(unit, IDEBus), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_ide_error_status, - .needed = ide_error_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_ide_error_status, + NULL } }; diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 4b5e32d..4afd0cf 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -350,6 +350,7 @@ static const VMStateDescription vmstate_bmdma_current = { .name = "ide bmdma_current", .version_id = 1, .minimum_version_id = 1, + .needed = ide_bmdma_current_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(cur_addr, BMDMAState), VMSTATE_UINT32(cur_prd_last, BMDMAState), @@ -363,6 +364,7 @@ static const VMStateDescription vmstate_bmdma_status = { .name ="ide bmdma/status", .version_id = 1, .minimum_version_id = 1, + .needed = ide_bmdma_status_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(status, BMDMAState), VMSTATE_END_OF_LIST() @@ -383,16 +385,10 @@ static const VMStateDescription vmstate_bmdma = { VMSTATE_UINT8(migration_retry_unit, BMDMAState), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_bmdma_current, - .needed = ide_bmdma_current_needed, - }, { - .vmsd = &vmstate_bmdma_status, - .needed = ide_bmdma_status_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_bmdma_current, + &vmstate_bmdma_status, + NULL } }; diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 9b9a7d7..ddac69d 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -391,23 +391,24 @@ static int kbd_outport_post_load(void *opaque, int version_id) return 0; } +static bool kbd_outport_needed(void *opaque) +{ + KBDState *s = opaque; + return s->outport != kbd_outport_default(s); +} + static const VMStateDescription vmstate_kbd_outport = { .name = "pckbd_outport", .version_id = 1, .minimum_version_id = 1, .post_load = kbd_outport_post_load, + .needed = kbd_outport_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(outport, KBDState), VMSTATE_END_OF_LIST() } }; -static bool kbd_outport_needed(void *opaque) -{ - KBDState *s = opaque; - return s->outport != kbd_outport_default(s); -} - static int kbd_post_load(void *opaque, int version_id) { KBDState *s = opaque; @@ -430,12 +431,9 @@ static const VMStateDescription vmstate_kbd = { VMSTATE_UINT8(pending, KBDState), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_kbd_outport, - .needed = kbd_outport_needed, - }, - VMSTATE_END_OF_LIST() + .subsections = (const VMStateDescription*[]) { + &vmstate_kbd_outport, + NULL } }; diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 4baeea2..fdbe565 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -677,6 +677,7 @@ static const VMStateDescription vmstate_ps2_keyboard_ledstate = { .version_id = 3, .minimum_version_id = 2, .post_load = ps2_kbd_ledstate_post_load, + .needed = ps2_keyboard_ledstate_needed, .fields = (VMStateField[]) { VMSTATE_INT32(ledstate, PS2KbdState), VMSTATE_END_OF_LIST() @@ -717,13 +718,9 @@ static const VMStateDescription vmstate_ps2_keyboard = { VMSTATE_INT32_V(scancode_set, PS2KbdState,3), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_ps2_keyboard_ledstate, - .needed = ps2_keyboard_ledstate_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_ps2_keyboard_ledstate, + NULL } }; diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index d595d63..0032b97 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -369,6 +369,7 @@ static const VMStateDescription vmstate_apic_common_sipi = { .name = "apic_sipi", .version_id = 1, .minimum_version_id = 1, + .needed = apic_common_sipi_needed, .fields = (VMStateField[]) { VMSTATE_INT32(sipi_vector, APICCommonState), VMSTATE_INT32(wait_for_sipi, APICCommonState), @@ -408,12 +409,9 @@ static const VMStateDescription vmstate_apic_common = { APICCommonState), /* open-coded timer state */ VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_apic_common_sipi, - .needed = apic_common_sipi_needed, - }, - VMSTATE_END_OF_LIST() + .subsections = (const VMStateDescription*[]) { + &vmstate_apic_common_sipi, + NULL } }; diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index c1d2e70..454bfd7 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -71,7 +71,7 @@ void gic_update(GICState *s) || !(s->cpu_ctlr[cpu] & (GICC_CTLR_EN_GRP0 | GICC_CTLR_EN_GRP1))) { qemu_irq_lower(s->parent_irq[cpu]); qemu_irq_lower(s->parent_fiq[cpu]); - return; + continue; } best_prio = 0x100; best_irq = 1023; diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 71a9f7a..b3e0b1f 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -650,6 +650,7 @@ static const VMStateDescription vmstate_ich9_rst_cnt = { .name = "ICH9LPC/rst_cnt", .version_id = 1, .minimum_version_id = 1, + .needed = ich9_rst_cnt_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(rst_cnt, ICH9LPCState), VMSTATE_END_OF_LIST() @@ -669,12 +670,9 @@ static const VMStateDescription vmstate_ich9_lpc = { VMSTATE_UINT32(sci_level, ICH9LPCState), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_ich9_rst_cnt, - .needed = ich9_rst_cnt_needed - }, - { 0 } + .subsections = (const VMStateDescription*[]) { + &vmstate_ich9_rst_cnt, + NULL } }; diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs index 0a652f8..9633f3a 100644 --- a/hw/mips/Makefile.objs +++ b/hw/mips/Makefile.objs @@ -1,4 +1,5 @@ -obj-y += mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o +obj-y += mips_r4k.o mips_malta.o mips_mipssim.o obj-y += addr.o cputimer.o mips_int.o +obj-$(CONFIG_JAZZ) += mips_jazz.o obj-$(CONFIG_FULONG) += mips_fulong2e.o obj-y += gt64xxx_pci.o diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index 2c153e0..9d60633 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -135,16 +135,16 @@ static void mips_jazz_init(MachineState *machine, MIPSCPU *cpu; CPUClass *cc; CPUMIPSState *env; - qemu_irq *rc4030, *i8259; + qemu_irq *i8259; rc4030_dma *dmas; - void* rc4030_opaque; + MemoryRegion *rc4030_dma_mr; MemoryRegion *isa_mem = g_new(MemoryRegion, 1); MemoryRegion *isa_io = g_new(MemoryRegion, 1); MemoryRegion *rtc = g_new(MemoryRegion, 1); MemoryRegion *i8042 = g_new(MemoryRegion, 1); MemoryRegion *dma_dummy = g_new(MemoryRegion, 1); NICInfo *nd; - DeviceState *dev; + DeviceState *dev, *rc4030; SysBusDevice *sysbus; ISABus *isa_bus; ISADevice *pit; @@ -157,12 +157,7 @@ static void mips_jazz_init(MachineState *machine, /* init CPUs */ if (cpu_model == NULL) { -#ifdef TARGET_MIPS64 cpu_model = "R4000"; -#else - /* FIXME: All wrong, this maybe should be R3000 for the older JAZZs. */ - cpu_model = "24Kf"; -#endif } cpu = cpu_mips_init(cpu_model); if (cpu == NULL) { @@ -218,8 +213,14 @@ static void mips_jazz_init(MachineState *machine, cpu_mips_clock_init(env); /* Chipset */ - rc4030_opaque = rc4030_init(env->irq[6], env->irq[3], &rc4030, &dmas, - address_space); + rc4030 = rc4030_init(&dmas, &rc4030_dma_mr); + sysbus = SYS_BUS_DEVICE(rc4030); + sysbus_connect_irq(sysbus, 0, env->irq[6]); + sysbus_connect_irq(sysbus, 1, env->irq[3]); + memory_region_add_subregion(address_space, 0x80000000, + sysbus_mmio_get_region(sysbus, 0)); + memory_region_add_subregion(address_space, 0xf0000000, + sysbus_mmio_get_region(sysbus, 1)); memory_region_init_io(dma_dummy, NULL, &dma_dummy_ops, NULL, "dummy_dma", 0x1000); memory_region_add_subregion(address_space, 0x8000d000, dma_dummy); @@ -246,7 +247,7 @@ static void mips_jazz_init(MachineState *machine, sysbus = SYS_BUS_DEVICE(dev); sysbus_mmio_map(sysbus, 0, 0x60080000); sysbus_mmio_map(sysbus, 1, 0x40000000); - sysbus_connect_irq(sysbus, 0, rc4030[3]); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 3)); { /* Simple ROM, so user doesn't have to provide one */ MemoryRegion *rom_mr = g_new(MemoryRegion, 1); @@ -272,8 +273,17 @@ static void mips_jazz_init(MachineState *machine, if (!nd->model) nd->model = g_strdup("dp83932"); if (strcmp(nd->model, "dp83932") == 0) { - dp83932_init(nd, 0x80001000, 2, get_system_memory(), rc4030[4], - rc4030_opaque, rc4030_dma_memory_rw); + qemu_check_nic_model(nd, "dp83932"); + + dev = qdev_create(NULL, "dp8393x"); + qdev_set_nic_properties(dev, nd); + qdev_prop_set_uint8(dev, "it_shift", 2); + qdev_prop_set_ptr(dev, "dma_mr", rc4030_dma_mr); + qdev_init_nofail(dev); + sysbus = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(sysbus, 0, 0x80001000); + sysbus_mmio_map(sysbus, 1, 0x8000b000); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 4)); break; } else if (is_help_option(nd->model)) { fprintf(stderr, "qemu: Supported NICs: dp83932\n"); @@ -287,7 +297,7 @@ static void mips_jazz_init(MachineState *machine, /* SCSI adapter */ esp_init(0x80002000, 0, rc4030_dma_read, rc4030_dma_write, dmas[0], - rc4030[5], &esp_reset, &dma_enable); + qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable); /* Floppy */ if (drive_get_max_bus(IF_FLOPPY) >= MAX_FD) { @@ -297,7 +307,7 @@ static void mips_jazz_init(MachineState *machine, for (n = 0; n < MAX_FD; n++) { fds[n] = drive_get(IF_FLOPPY, 0, n); } - fdctrl_init_sysbus(rc4030[1], 0, 0x80003000, fds); + fdctrl_init_sysbus(qdev_get_gpio_in(rc4030, 1), 0, 0x80003000, fds); /* Real time clock */ rtc_init(isa_bus, 1980, NULL); @@ -305,23 +315,26 @@ static void mips_jazz_init(MachineState *machine, memory_region_add_subregion(address_space, 0x80004000, rtc); /* Keyboard (i8042) */ - i8042_mm_init(rc4030[6], rc4030[7], i8042, 0x1000, 0x1); + i8042_mm_init(qdev_get_gpio_in(rc4030, 6), qdev_get_gpio_in(rc4030, 7), + i8042, 0x1000, 0x1); memory_region_add_subregion(address_space, 0x80005000, i8042); /* Serial ports */ if (serial_hds[0]) { - serial_mm_init(address_space, 0x80006000, 0, rc4030[8], 8000000/16, + serial_mm_init(address_space, 0x80006000, 0, + qdev_get_gpio_in(rc4030, 8), 8000000/16, serial_hds[0], DEVICE_NATIVE_ENDIAN); } if (serial_hds[1]) { - serial_mm_init(address_space, 0x80007000, 0, rc4030[9], 8000000/16, + serial_mm_init(address_space, 0x80007000, 0, + qdev_get_gpio_in(rc4030, 9), 8000000/16, serial_hds[1], DEVICE_NATIVE_ENDIAN); } /* Parallel port */ if (parallel_hds[0]) - parallel_mm_init(address_space, 0x80008000, 0, rc4030[0], - parallel_hds[0]); + parallel_mm_init(address_space, 0x80008000, 0, + qdev_get_gpio_in(rc4030, 0), parallel_hds[0]); /* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */ diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 5140882..786a8f0 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -97,7 +97,7 @@ typedef struct { static ISADevice *pit; static struct _loaderparams { - int ram_size; + int ram_size, ram_low_size; const char *kernel_filename; const char *kernel_cmdline; const char *initrd_filename; @@ -641,8 +641,8 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base, stl_p(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a1, low(ENVP_ADDR) */ stl_p(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */ stl_p(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */ - stl_p(p++, 0x3c070000 | (loaderparams.ram_size >> 16)); /* lui a3, high(ram_size) */ - stl_p(p++, 0x34e70000 | (loaderparams.ram_size & 0xffff)); /* ori a3, a3, low(ram_size) */ + stl_p(p++, 0x3c070000 | (loaderparams.ram_low_size >> 16)); /* lui a3, high(ram_low_size) */ + stl_p(p++, 0x34e70000 | (loaderparams.ram_low_size & 0xffff)); /* ori a3, a3, low(ram_low_size) */ /* Load BAR registers as done by YAMON */ stl_p(p++, 0x3c09b400); /* lui t1, 0xb400 */ @@ -851,8 +851,10 @@ static int64_t load_kernel (void) } prom_set(prom_buf, prom_index++, "memsize"); - prom_set(prom_buf, prom_index++, "%i", - MIN(loaderparams.ram_size, 256 << 20)); + prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_low_size); + + prom_set(prom_buf, prom_index++, "ememsize"); + prom_set(prom_buf, prom_index++, "%u", loaderparams.ram_size); prom_set(prom_buf, prom_index++, "modetty0"); prom_set(prom_buf, prom_index++, "38400n8r"); @@ -1054,7 +1056,8 @@ void mips_malta_init(MachineState *machine) } /* Write a small bootloader to the flash location. */ - loaderparams.ram_size = ram_low_size; + loaderparams.ram_size = ram_size; + loaderparams.ram_low_size = ram_low_size; loaderparams.kernel_filename = kernel_filename; loaderparams.kernel_cmdline = kernel_cmdline; loaderparams.initrd_filename = initrd_filename; diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 7b91c4e..9880173 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -39,3 +39,4 @@ obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \ rocker/rocker_desc.o rocker/rocker_world.o \ rocker/rocker_of_dpa.o +obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index 7ce13d2..cd889bc 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -17,20 +17,15 @@ * with this program; if not, see <http://www.gnu.org/licenses/>. */ -#include "hw/hw.h" -#include "qemu/timer.h" +#include "hw/sysbus.h" +#include "hw/devices.h" #include "net/net.h" -#include "hw/mips/mips.h" +#include "qemu/timer.h" +#include <zlib.h> //#define DEBUG_SONIC -/* Calculate CRCs properly on Rx packets */ -#define SONIC_CALCULATE_RXCRC - -#if defined(SONIC_CALCULATE_RXCRC) -/* For crc32 */ -#include <zlib.h> -#endif +#define SONIC_PROM_SIZE 0x1000 #ifdef DEBUG_SONIC #define DPRINTF(fmt, ...) \ @@ -145,9 +140,14 @@ do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0) #define SONIC_ISR_PINT 0x0800 #define SONIC_ISR_LCD 0x1000 +#define TYPE_DP8393X "dp8393x" +#define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X) + typedef struct dp8393xState { + SysBusDevice parent_obj; + /* Hardware */ - int it_shift; + uint8_t it_shift; qemu_irq irq; #ifdef DEBUG_SONIC int irq_level; @@ -156,8 +156,8 @@ typedef struct dp8393xState { int64_t wt_last_update; NICConf conf; NICState *nic; - MemoryRegion *address_space; MemoryRegion mmio; + MemoryRegion prom; /* Registers */ uint8_t cam[16][6]; @@ -168,8 +168,8 @@ typedef struct dp8393xState { int loopback_packet; /* Memory access */ - void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write); - void* mem_opaque; + void *dma_mr; + AddressSpace as; } dp8393xState; static void dp8393x_update_irq(dp8393xState *s) @@ -190,7 +190,7 @@ static void dp8393x_update_irq(dp8393xState *s) qemu_set_irq(s->irq, level); } -static void do_load_cam(dp8393xState *s) +static void dp8393x_do_load_cam(dp8393xState *s) { uint16_t data[8]; int width, size; @@ -201,9 +201,9 @@ static void do_load_cam(dp8393xState *s) while (s->regs[SONIC_CDC] & 0x1f) { /* Fill current entry */ - s->memory_rw(s->mem_opaque, + address_space_rw(&s->as, (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], - (uint8_t *)data, size, 0); + MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->cam[index][0] = data[1 * width] & 0xff; s->cam[index][1] = data[1 * width] >> 8; s->cam[index][2] = data[2 * width] & 0xff; @@ -220,9 +220,9 @@ static void do_load_cam(dp8393xState *s) } /* Read CAM enable */ - s->memory_rw(s->mem_opaque, + address_space_rw(&s->as, (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], - (uint8_t *)data, size, 0); + MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_CE] = data[0 * width]; DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]); @@ -232,7 +232,7 @@ static void do_load_cam(dp8393xState *s) dp8393x_update_irq(s); } -static void do_read_rra(dp8393xState *s) +static void dp8393x_do_read_rra(dp8393xState *s) { uint16_t data[8]; int width, size; @@ -240,9 +240,9 @@ static void do_read_rra(dp8393xState *s) /* Read memory */ width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; size = sizeof(uint16_t) * 4 * width; - s->memory_rw(s->mem_opaque, + address_space_rw(&s->as, (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP], - (uint8_t *)data, size, 0); + MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); /* Update SONIC registers */ s->regs[SONIC_CRBA0] = data[0 * width]; @@ -272,7 +272,7 @@ static void do_read_rra(dp8393xState *s) s->regs[SONIC_CR] &= ~SONIC_CR_RRRA; } -static void do_software_reset(dp8393xState *s) +static void dp8393x_do_software_reset(dp8393xState *s) { timer_del(s->watchdog); @@ -280,7 +280,7 @@ static void do_software_reset(dp8393xState *s) s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS; } -static void set_next_tick(dp8393xState *s) +static void dp8393x_set_next_tick(dp8393xState *s) { uint32_t ticks; int64_t delay; @@ -296,7 +296,7 @@ static void set_next_tick(dp8393xState *s) timer_mod(s->watchdog, s->wt_last_update + delay); } -static void update_wt_regs(dp8393xState *s) +static void dp8393x_update_wt_regs(dp8393xState *s) { int64_t elapsed; uint32_t val; @@ -311,33 +311,33 @@ static void update_wt_regs(dp8393xState *s) val -= elapsed / 5000000; s->regs[SONIC_WT1] = (val >> 16) & 0xffff; s->regs[SONIC_WT0] = (val >> 0) & 0xffff; - set_next_tick(s); + dp8393x_set_next_tick(s); } -static void do_start_timer(dp8393xState *s) +static void dp8393x_do_start_timer(dp8393xState *s) { s->regs[SONIC_CR] &= ~SONIC_CR_STP; - set_next_tick(s); + dp8393x_set_next_tick(s); } -static void do_stop_timer(dp8393xState *s) +static void dp8393x_do_stop_timer(dp8393xState *s) { s->regs[SONIC_CR] &= ~SONIC_CR_ST; - update_wt_regs(s); + dp8393x_update_wt_regs(s); } -static void do_receiver_enable(dp8393xState *s) +static void dp8393x_do_receiver_enable(dp8393xState *s) { s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS; } -static void do_receiver_disable(dp8393xState *s) +static void dp8393x_do_receiver_disable(dp8393xState *s) { s->regs[SONIC_CR] &= ~SONIC_CR_RXEN; } -static void do_transmit_packets(dp8393xState *s) +static void dp8393x_do_transmit_packets(dp8393xState *s) { NetClientState *nc = qemu_get_queue(s->nic); uint16_t data[12]; @@ -353,9 +353,9 @@ static void do_transmit_packets(dp8393xState *s) (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]); size = sizeof(uint16_t) * 6 * width; s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; - s->memory_rw(s->mem_opaque, + address_space_rw(&s->as, ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width, - (uint8_t *)data, size, 0); + MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); tx_len = 0; /* Update registers */ @@ -379,18 +379,18 @@ static void do_transmit_packets(dp8393xState *s) if (tx_len + len > sizeof(s->tx_buffer)) { len = sizeof(s->tx_buffer) - tx_len; } - s->memory_rw(s->mem_opaque, + address_space_rw(&s->as, (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0], - &s->tx_buffer[tx_len], len, 0); + MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0); tx_len += len; i++; if (i != s->regs[SONIC_TFC]) { /* Read next fragment details */ size = sizeof(uint16_t) * 3 * width; - s->memory_rw(s->mem_opaque, + address_space_rw(&s->as, ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width, - (uint8_t *)data, size, 0); + MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_TSA0] = data[0 * width]; s->regs[SONIC_TSA1] = data[1 * width]; s->regs[SONIC_TFS] = data[2 * width]; @@ -422,16 +422,16 @@ static void do_transmit_packets(dp8393xState *s) /* Write status */ data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */ size = sizeof(uint16_t) * width; - s->memory_rw(s->mem_opaque, + address_space_rw(&s->as, (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA], - (uint8_t *)data, size, 1); + MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1); if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { /* Read footer of packet */ size = sizeof(uint16_t) * width; - s->memory_rw(s->mem_opaque, + address_space_rw(&s->as, ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width, - (uint8_t *)data, size, 0); + MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_CTDA] = data[0 * width] & ~0x1; if (data[0 * width] & 0x1) { /* EOL detected */ @@ -446,12 +446,12 @@ static void do_transmit_packets(dp8393xState *s) dp8393x_update_irq(s); } -static void do_halt_transmission(dp8393xState *s) +static void dp8393x_do_halt_transmission(dp8393xState *s) { /* Nothing to do */ } -static void do_command(dp8393xState *s, uint16_t command) +static void dp8393x_do_command(dp8393xState *s, uint16_t command) { if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) { s->regs[SONIC_CR] &= ~SONIC_CR_RST; @@ -461,34 +461,36 @@ static void do_command(dp8393xState *s, uint16_t command) s->regs[SONIC_CR] |= (command & SONIC_CR_MASK); if (command & SONIC_CR_HTX) - do_halt_transmission(s); + dp8393x_do_halt_transmission(s); if (command & SONIC_CR_TXP) - do_transmit_packets(s); + dp8393x_do_transmit_packets(s); if (command & SONIC_CR_RXDIS) - do_receiver_disable(s); + dp8393x_do_receiver_disable(s); if (command & SONIC_CR_RXEN) - do_receiver_enable(s); + dp8393x_do_receiver_enable(s); if (command & SONIC_CR_STP) - do_stop_timer(s); + dp8393x_do_stop_timer(s); if (command & SONIC_CR_ST) - do_start_timer(s); + dp8393x_do_start_timer(s); if (command & SONIC_CR_RST) - do_software_reset(s); + dp8393x_do_software_reset(s); if (command & SONIC_CR_RRRA) - do_read_rra(s); + dp8393x_do_read_rra(s); if (command & SONIC_CR_LCAM) - do_load_cam(s); + dp8393x_do_load_cam(s); } -static uint16_t read_register(dp8393xState *s, int reg) +static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size) { + dp8393xState *s = opaque; + int reg = addr >> s->it_shift; uint16_t val = 0; switch (reg) { /* Update data before reading it */ case SONIC_WT0: case SONIC_WT1: - update_wt_regs(s); + dp8393x_update_wt_regs(s); val = s->regs[reg]; break; /* Accept read to some registers only when in reset mode */ @@ -510,14 +512,18 @@ static uint16_t read_register(dp8393xState *s, int reg) return val; } -static void write_register(dp8393xState *s, int reg, uint16_t val) +static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) { - DPRINTF("write 0x%04x to reg %s\n", val, reg_names[reg]); + dp8393xState *s = opaque; + int reg = addr >> s->it_shift; + + DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]); switch (reg) { /* Command register */ case SONIC_CR: - do_command(s, val); + dp8393x_do_command(s, data); break; /* Prevent write to read-only registers */ case SONIC_CAP2: @@ -530,37 +536,37 @@ static void write_register(dp8393xState *s, int reg, uint16_t val) /* Accept write to some registers only when in reset mode */ case SONIC_DCR: if (s->regs[SONIC_CR] & SONIC_CR_RST) { - s->regs[reg] = val & 0xbfff; + s->regs[reg] = data & 0xbfff; } else { DPRINTF("writing to DCR invalid\n"); } break; case SONIC_DCR2: if (s->regs[SONIC_CR] & SONIC_CR_RST) { - s->regs[reg] = val & 0xf017; + s->regs[reg] = data & 0xf017; } else { DPRINTF("writing to DCR2 invalid\n"); } break; /* 12 lower bytes are Read Only */ case SONIC_TCR: - s->regs[reg] = val & 0xf000; + s->regs[reg] = data & 0xf000; break; /* 9 lower bytes are Read Only */ case SONIC_RCR: - s->regs[reg] = val & 0xffe0; + s->regs[reg] = data & 0xffe0; break; /* Ignore most significant bit */ case SONIC_IMR: - s->regs[reg] = val & 0x7fff; + s->regs[reg] = data & 0x7fff; dp8393x_update_irq(s); break; /* Clear bits by writing 1 to them */ case SONIC_ISR: - val &= s->regs[reg]; - s->regs[reg] &= ~val; - if (val & SONIC_ISR_RBE) { - do_read_rra(s); + data &= s->regs[reg]; + s->regs[reg] &= ~data; + if (data & SONIC_ISR_RBE) { + dp8393x_do_read_rra(s); } dp8393x_update_irq(s); break; @@ -569,24 +575,32 @@ static void write_register(dp8393xState *s, int reg, uint16_t val) case SONIC_REA: case SONIC_RRP: case SONIC_RWP: - s->regs[reg] = val & 0xfffe; + s->regs[reg] = data & 0xfffe; break; /* Invert written value for some registers */ case SONIC_CRCT: case SONIC_FAET: case SONIC_MPT: - s->regs[reg] = val ^ 0xffff; + s->regs[reg] = data ^ 0xffff; break; /* All other registers have no special contrainst */ default: - s->regs[reg] = val; + s->regs[reg] = data; } if (reg == SONIC_WT0 || reg == SONIC_WT1) { - set_next_tick(s); + dp8393x_set_next_tick(s); } } +static const MemoryRegionOps dp8393x_ops = { + .read = dp8393x_read, + .write = dp8393x_write, + .impl.min_access_size = 2, + .impl.max_access_size = 2, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + static void dp8393x_watchdog(void *opaque) { dp8393xState *s = opaque; @@ -597,84 +611,14 @@ static void dp8393x_watchdog(void *opaque) s->regs[SONIC_WT1] = 0xffff; s->regs[SONIC_WT0] = 0xffff; - set_next_tick(s); + dp8393x_set_next_tick(s); /* Signal underflow */ s->regs[SONIC_ISR] |= SONIC_ISR_TC; dp8393x_update_irq(s); } -static uint32_t dp8393x_readw(void *opaque, hwaddr addr) -{ - dp8393xState *s = opaque; - int reg; - - if ((addr & ((1 << s->it_shift) - 1)) != 0) { - return 0; - } - - reg = addr >> s->it_shift; - return read_register(s, reg); -} - -static uint32_t dp8393x_readb(void *opaque, hwaddr addr) -{ - uint16_t v = dp8393x_readw(opaque, addr & ~0x1); - return (v >> (8 * (addr & 0x1))) & 0xff; -} - -static uint32_t dp8393x_readl(void *opaque, hwaddr addr) -{ - uint32_t v; - v = dp8393x_readw(opaque, addr); - v |= dp8393x_readw(opaque, addr + 2) << 16; - return v; -} - -static void dp8393x_writew(void *opaque, hwaddr addr, uint32_t val) -{ - dp8393xState *s = opaque; - int reg; - - if ((addr & ((1 << s->it_shift) - 1)) != 0) { - return; - } - - reg = addr >> s->it_shift; - - write_register(s, reg, (uint16_t)val); -} - -static void dp8393x_writeb(void *opaque, hwaddr addr, uint32_t val) -{ - uint16_t old_val = dp8393x_readw(opaque, addr & ~0x1); - - switch (addr & 3) { - case 0: - val = val | (old_val & 0xff00); - break; - case 1: - val = (val << 8) | (old_val & 0x00ff); - break; - } - dp8393x_writew(opaque, addr & ~0x1, val); -} - -static void dp8393x_writel(void *opaque, hwaddr addr, uint32_t val) -{ - dp8393x_writew(opaque, addr, val & 0xffff); - dp8393x_writew(opaque, addr + 2, (val >> 16) & 0xffff); -} - -static const MemoryRegionOps dp8393x_ops = { - .old_mmio = { - .read = { dp8393x_readb, dp8393x_readw, dp8393x_readl, }, - .write = { dp8393x_writeb, dp8393x_writew, dp8393x_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int nic_can_receive(NetClientState *nc) +static int dp8393x_can_receive(NetClientState *nc) { dp8393xState *s = qemu_get_nic_opaque(nc); @@ -685,7 +629,8 @@ static int nic_can_receive(NetClientState *nc) return 1; } -static int receive_filter(dp8393xState *s, const uint8_t * buf, int size) +static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf, + int size) { static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; int i; @@ -723,7 +668,8 @@ static int receive_filter(dp8393xState *s, const uint8_t * buf, int size) return -1; } -static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) +static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, + size_t size) { dp8393xState *s = qemu_get_nic_opaque(nc); uint16_t data[10]; @@ -737,7 +683,7 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER | SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC); - packet_type = receive_filter(s, buf, size); + packet_type = dp8393x_receive_filter(s, buf, size); if (packet_type < 0) { DPRINTF("packet not for netcard\n"); return -1; @@ -750,7 +696,8 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) /* Are we still in resource exhaustion? */ size = sizeof(uint16_t) * 1 * width; address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width; - s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0); + address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, + (uint8_t *)data, size, 0); if (data[0 * width] & 0x1) { /* Still EOL ; stop reception */ return -1; @@ -764,18 +711,16 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0]; /* Calculate the ethernet checksum */ -#ifdef SONIC_CALCULATE_RXCRC checksum = cpu_to_le32(crc32(0, buf, rx_len)); -#else - checksum = 0; -#endif /* Put packet into RBA */ DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]); address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]; - s->memory_rw(s->mem_opaque, address, (uint8_t*)buf, rx_len, 1); + address_space_rw(&s->as, address, + MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1); address += rx_len; - s->memory_rw(s->mem_opaque, address, (uint8_t*)&checksum, 4, 1); + address_space_rw(&s->as, address, + MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1); rx_len += 4; s->regs[SONIC_CRBA1] = address >> 16; s->regs[SONIC_CRBA0] = address & 0xffff; @@ -803,29 +748,30 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */ data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */ size = sizeof(uint16_t) * 5 * width; - s->memory_rw(s->mem_opaque, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], (uint8_t *)data, size, 1); + address_space_rw(&s->as, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], + MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1); /* Move to next descriptor */ size = sizeof(uint16_t) * width; - s->memory_rw(s->mem_opaque, + address_space_rw(&s->as, ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width, - (uint8_t *)data, size, 0); + MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_LLFA] = data[0 * width]; if (s->regs[SONIC_LLFA] & 0x1) { /* EOL detected */ s->regs[SONIC_ISR] |= SONIC_ISR_RDE; } else { data[0 * width] = 0; /* in_use */ - s->memory_rw(s->mem_opaque, + address_space_rw(&s->as, ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width, - (uint8_t *)data, size, 1); + MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1); s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff); if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { /* Read next RRA */ - do_read_rra(s); + dp8393x_do_read_rra(s); } } @@ -835,11 +781,12 @@ static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size) return size; } -static void nic_reset(void *opaque) +static void dp8393x_reset(DeviceState *dev) { - dp8393xState *s = opaque; + dp8393xState *s = DP8393X(dev); timer_del(s->watchdog); + memset(s->regs, 0, sizeof(s->regs)); s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS; s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR); s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT); @@ -862,39 +809,91 @@ static void nic_reset(void *opaque) static NetClientInfo net_dp83932_info = { .type = NET_CLIENT_OPTIONS_KIND_NIC, .size = sizeof(NICState), - .can_receive = nic_can_receive, - .receive = nic_receive, + .can_receive = dp8393x_can_receive, + .receive = dp8393x_receive, }; -void dp83932_init(NICInfo *nd, hwaddr base, int it_shift, - MemoryRegion *address_space, - qemu_irq irq, void* mem_opaque, - void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write)) +static void dp8393x_instance_init(Object *obj) { - dp8393xState *s; + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + dp8393xState *s = DP8393X(obj); + + sysbus_init_mmio(sbd, &s->mmio); + sysbus_init_mmio(sbd, &s->prom); + sysbus_init_irq(sbd, &s->irq); +} - qemu_check_nic_model(nd, "dp83932"); +static void dp8393x_realize(DeviceState *dev, Error **errp) +{ + dp8393xState *s = DP8393X(dev); + int i, checksum; + uint8_t *prom; - s = g_malloc0(sizeof(dp8393xState)); + address_space_init(&s->as, s->dma_mr, "dp8393x"); + memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s, + "dp8393x-regs", 0x40 << s->it_shift); + + s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, + object_get_typename(OBJECT(dev)), dev->id, s); + qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - s->address_space = address_space; - s->mem_opaque = mem_opaque; - s->memory_rw = memory_rw; - s->it_shift = it_shift; - s->irq = irq; s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - s->conf.macaddr = nd->macaddr; - s->conf.peers.ncs[0] = nd->netdev; + memory_region_init_rom_device(&s->prom, OBJECT(dev), NULL, NULL, + "dp8393x-prom", SONIC_PROM_SIZE, NULL); + prom = memory_region_get_ram_ptr(&s->prom); + checksum = 0; + for (i = 0; i < 6; i++) { + prom[i] = s->conf.macaddr.a[i]; + checksum += prom[i]; + if (checksum > 0xff) { + checksum = (checksum + 1) & 0xff; + } + } + prom[7] = 0xff - checksum; +} - s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s); +static const VMStateDescription vmstate_dp8393x = { + .name = "dp8393x", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField []) { + VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6), + VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40), + VMSTATE_END_OF_LIST() + } +}; - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - qemu_register_reset(nic_reset, s); - nic_reset(s); +static Property dp8393x_properties[] = { + DEFINE_NIC_PROPERTIES(dp8393xState, conf), + DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr), + DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void dp8393x_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); - memory_region_init_io(&s->mmio, NULL, &dp8393x_ops, s, - "dp8393x", 0x40 << it_shift); - memory_region_add_subregion(address_space, base, &s->mmio); + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + dc->realize = dp8393x_realize; + dc->reset = dp8393x_reset; + dc->vmsd = &vmstate_dp8393x; + dc->props = dp8393x_properties; } + +static const TypeInfo dp8393x_info = { + .name = TYPE_DP8393X, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(dp8393xState), + .instance_init = dp8393x_instance_init, + .class_init = dp8393x_class_init, +}; + +static void dp8393x_register_types(void) +{ + type_register_static(&dp8393x_info); +} + +type_init(dp8393x_register_types) diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 091d61a..bab8e2a 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1370,6 +1370,7 @@ static const VMStateDescription vmstate_e1000_mit_state = { .name = "e1000/mit_state", .version_id = 1, .minimum_version_id = 1, + .needed = e1000_mit_state_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(mac_reg[RDTR], E1000State), VMSTATE_UINT32(mac_reg[RADV], E1000State), @@ -1457,13 +1458,9 @@ static const VMStateDescription vmstate_e1000 = { VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_e1000_mit_state, - .needed = e1000_mit_state_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_e1000_mit_state, + NULL } }; diff --git a/hw/net/rocker/qmp-norocker.c b/hw/net/rocker/qmp-norocker.c new file mode 100644 index 0000000..f253747 --- /dev/null +++ b/hw/net/rocker/qmp-norocker.c @@ -0,0 +1,50 @@ +/* + * QMP Target options - Commands handled based on a target config + * versus a host config + * + * Copyright (c) 2015 David Ahern <dsahern@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "qemu-common.h" +#include "qmp-commands.h" +#include "qapi/qmp/qerror.h" + +RockerSwitch *qmp_query_rocker(const char *name, Error **errp) +{ + error_set(errp, QERR_FEATURE_DISABLED, "rocker"); + return NULL; +}; + +RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp) +{ + error_set(errp, QERR_FEATURE_DISABLED, "rocker"); + return NULL; +}; + +RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name, + bool has_tbl_id, + uint32_t tbl_id, + Error **errp) +{ + error_set(errp, QERR_FEATURE_DISABLED, "rocker"); + return NULL; +}; + +RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, + bool has_type, + uint8_t type, + Error **errp) +{ + error_set(errp, QERR_FEATURE_DISABLED, "rocker"); + return NULL; +}; diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 55b6c46..4d25842 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -94,6 +94,51 @@ World *rocker_get_world(Rocker *r, enum rocker_world_type type) return NULL; } +RockerSwitch *qmp_query_rocker(const char *name, Error **errp) +{ + RockerSwitch *rocker = g_malloc0(sizeof(*rocker)); + Rocker *r; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + rocker->name = g_strdup(r->name); + rocker->id = r->switch_id; + rocker->ports = r->fp_ports; + + return rocker; +} + +RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp) +{ + RockerPortList *list = NULL; + Rocker *r; + int i; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + for (i = r->fp_ports - 1; i >= 0; i--) { + RockerPortList *info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + struct fp_port *port = r->fp_port[i]; + + fp_port_get_info(port, info); + info->next = list; + list = info; + } + + return list; +} + uint32_t rocker_fp_ports(Rocker *r) { return r->fp_ports; @@ -238,6 +283,7 @@ static int cmd_get_port_settings(Rocker *r, uint8_t duplex; uint8_t autoneg; uint8_t learning; + char *phys_name; MACAddr macaddr; enum rocker_world_type mode; size_t tlv_size; @@ -265,6 +311,7 @@ static int cmd_get_port_settings(Rocker *r, fp_port_get_macaddr(fp_port, &macaddr); mode = world_type(fp_port_get_world(fp_port)); learning = fp_port_get_learning(fp_port); + phys_name = fp_port_get_name(fp_port); tlv_size = rocker_tlv_total_size(0) + /* nest */ rocker_tlv_total_size(sizeof(uint32_t)) + /* pport */ @@ -273,7 +320,8 @@ static int cmd_get_port_settings(Rocker *r, rocker_tlv_total_size(sizeof(uint8_t)) + /* autoneg */ rocker_tlv_total_size(sizeof(macaddr.a)) + /* macaddr */ rocker_tlv_total_size(sizeof(uint8_t)) + /* mode */ - rocker_tlv_total_size(sizeof(uint8_t)); /* learning */ + rocker_tlv_total_size(sizeof(uint8_t)) + /* learning */ + rocker_tlv_total_size(strlen(phys_name)); if (tlv_size > desc_buf_size(info)) { return -ROCKER_EMSGSIZE; @@ -290,6 +338,8 @@ static int cmd_get_port_settings(Rocker *r, rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MODE, mode); rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, learning); + rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME, + strlen(phys_name), phys_name); rocker_tlv_nest_end(buf, &pos, nest); return desc_set_buf(info, tlv_size); @@ -1277,6 +1327,22 @@ static int pci_rocker_init(PCIDevice *dev) goto err_duplicate; } + /* Rocker name is passed in port name requests to OS with the intention + * that the name is used in interface names. Limit the length of the + * rocker name to avoid naming problems in the OS. Also, adding the + * port number as p# and unganged breakout b#, where # is at most 2 + * digits, so leave room for it too (-1 for string terminator, -3 for + * p# and -3 for b#) + */ +#define ROCKER_IFNAMSIZ 16 +#define MAX_ROCKER_NAME_LEN (ROCKER_IFNAMSIZ - 1 - 3 - 3) + if (strlen(r->name) > MAX_ROCKER_NAME_LEN) { + fprintf(stderr, + "rocker: name too long; please shorten to at most %d chars\n", + MAX_ROCKER_NAME_LEN); + return -EINVAL; + } + if (memcmp(&r->fp_start_macaddr, &zero, sizeof(zero)) == 0) { memcpy(&r->fp_start_macaddr, &dflt, sizeof(dflt)); r->fp_start_macaddr.a[4] += (sw_index++); diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c index 2f1e3b3..d8d934c 100644 --- a/hw/net/rocker/rocker_fp.c +++ b/hw/net/rocker/rocker_fp.c @@ -41,11 +41,26 @@ struct fp_port { NICConf conf; }; +char *fp_port_get_name(FpPort *port) +{ + return port->name; +} + bool fp_port_get_link_up(FpPort *port) { return !qemu_get_queue(port->nic)->link_down; } +void fp_port_get_info(FpPort *port, RockerPortList *info) +{ + info->value->name = g_strdup(port->name); + info->value->enabled = port->enabled; + info->value->link_up = fp_port_get_link_up(port); + info->value->speed = port->speed; + info->value->duplex = port->duplex; + info->value->autoneg = port->autoneg; +} + void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr) { memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a)); @@ -173,8 +188,19 @@ bool fp_port_enabled(FpPort *port) return port->enabled; } +static void fp_port_set_link(FpPort *port, bool up) +{ + NetClientState *nc = qemu_get_queue(port->nic); + + if (up == nc->link_down) { + nc->link_down = !up; + nc->info->link_status_changed(nc); + } +} + void fp_port_enable(FpPort *port) { + fp_port_set_link(port, true); port->enabled = true; DPRINTF("port %d enabled\n", port->index); } @@ -182,6 +208,7 @@ void fp_port_enable(FpPort *port) void fp_port_disable(FpPort *port) { port->enabled = false; + fp_port_set_link(port, false); DPRINTF("port %d disabled\n", port->index); } @@ -201,7 +228,7 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name, /* front-panel switch port names are 1-based */ - port->name = g_strdup_printf("%s.%d", sw_name, port->pport); + port->name = g_strdup_printf("%sp%d", sw_name, port->pport); memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a)); port->conf.macaddr.a[5] += index; diff --git a/hw/net/rocker/rocker_fp.h b/hw/net/rocker/rocker_fp.h index a5f28f1..ab80fd8 100644 --- a/hw/net/rocker/rocker_fp.h +++ b/hw/net/rocker/rocker_fp.h @@ -26,7 +26,9 @@ typedef struct fp_port FpPort; int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt); +char *fp_port_get_name(FpPort *port); bool fp_port_get_link_up(FpPort *port); +void fp_port_get_info(FpPort *port, RockerPortList *info); void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr); void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr); uint8_t fp_port_get_learning(FpPort *port); diff --git a/hw/net/rocker/rocker_hw.h b/hw/net/rocker/rocker_hw.h index c9c85a7..fe639ba 100644 --- a/hw/net/rocker/rocker_hw.h +++ b/hw/net/rocker/rocker_hw.h @@ -179,6 +179,7 @@ enum { ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR, /* binary */ ROCKER_TLV_CMD_PORT_SETTINGS_MODE, /* u8 */ ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, /* u8 */ + ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME, /* binary */ __ROCKER_TLV_CMD_PORT_SETTINGS_MAX, ROCKER_TLV_CMD_PORT_SETTINGS_MAX = __ROCKER_TLV_CMD_PORT_SETTINGS_MAX - 1, diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index 1bcb7af..b25a17d 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -2302,6 +2302,318 @@ static void of_dpa_uninit(World *world) g_hash_table_destroy(of_dpa->flow_tbl); } +struct of_dpa_flow_fill_context { + RockerOfDpaFlowList *list; + uint32_t tbl_id; +}; + +static void of_dpa_flow_fill(void *cookie, void *value, void *user_data) +{ + struct of_dpa_flow *flow = value; + struct of_dpa_flow_key *key = &flow->key; + struct of_dpa_flow_key *mask = &flow->mask; + struct of_dpa_flow_fill_context *flow_context = user_data; + RockerOfDpaFlowList *new; + RockerOfDpaFlow *nflow; + RockerOfDpaFlowKey *nkey; + RockerOfDpaFlowMask *nmask; + RockerOfDpaFlowAction *naction; + + if (flow_context->tbl_id != -1 && + flow_context->tbl_id != key->tbl_id) { + return; + } + + new = g_malloc0(sizeof(*new)); + nflow = new->value = g_malloc0(sizeof(*nflow)); + nkey = nflow->key = g_malloc0(sizeof(*nkey)); + nmask = nflow->mask = g_malloc0(sizeof(*nmask)); + naction = nflow->action = g_malloc0(sizeof(*naction)); + + nflow->cookie = flow->cookie; + nflow->hits = flow->stats.hits; + nkey->priority = flow->priority; + nkey->tbl_id = key->tbl_id; + + if (key->in_pport || mask->in_pport) { + nkey->has_in_pport = true; + nkey->in_pport = key->in_pport; + } + + if (nkey->has_in_pport && mask->in_pport != 0xffffffff) { + nmask->has_in_pport = true; + nmask->in_pport = mask->in_pport; + } + + if (key->eth.vlan_id || mask->eth.vlan_id) { + nkey->has_vlan_id = true; + nkey->vlan_id = ntohs(key->eth.vlan_id); + } + + if (nkey->has_vlan_id && mask->eth.vlan_id != 0xffff) { + nmask->has_vlan_id = true; + nmask->vlan_id = ntohs(mask->eth.vlan_id); + } + + if (key->tunnel_id || mask->tunnel_id) { + nkey->has_tunnel_id = true; + nkey->tunnel_id = key->tunnel_id; + } + + if (nkey->has_tunnel_id && mask->tunnel_id != 0xffffffff) { + nmask->has_tunnel_id = true; + nmask->tunnel_id = mask->tunnel_id; + } + + if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) || + memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN)) { + nkey->has_eth_src = true; + nkey->eth_src = qemu_mac_strdup_printf(key->eth.src.a); + } + + if (nkey->has_eth_src && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) { + nmask->has_eth_src = true; + nmask->eth_src = qemu_mac_strdup_printf(mask->eth.src.a); + } + + if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) || + memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN)) { + nkey->has_eth_dst = true; + nkey->eth_dst = qemu_mac_strdup_printf(key->eth.dst.a); + } + + if (nkey->has_eth_dst && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) { + nmask->has_eth_dst = true; + nmask->eth_dst = qemu_mac_strdup_printf(mask->eth.dst.a); + } + + if (key->eth.type) { + + nkey->has_eth_type = true; + nkey->eth_type = ntohs(key->eth.type); + + switch (ntohs(key->eth.type)) { + case 0x0800: + case 0x86dd: + if (key->ip.proto || mask->ip.proto) { + nkey->has_ip_proto = true; + nkey->ip_proto = key->ip.proto; + } + if (nkey->has_ip_proto && mask->ip.proto != 0xff) { + nmask->has_ip_proto = true; + nmask->ip_proto = mask->ip.proto; + } + if (key->ip.tos || mask->ip.tos) { + nkey->has_ip_tos = true; + nkey->ip_tos = key->ip.tos; + } + if (nkey->has_ip_tos && mask->ip.tos != 0xff) { + nmask->has_ip_tos = true; + nmask->ip_tos = mask->ip.tos; + } + break; + } + + switch (ntohs(key->eth.type)) { + case 0x0800: + if (key->ipv4.addr.dst || mask->ipv4.addr.dst) { + char *dst = inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst); + int dst_len = of_dpa_mask2prefix(mask->ipv4.addr.dst); + nkey->has_ip_dst = true; + nkey->ip_dst = g_strdup_printf("%s/%d", dst, dst_len); + } + break; + } + } + + if (flow->action.goto_tbl) { + naction->has_goto_tbl = true; + naction->goto_tbl = flow->action.goto_tbl; + } + + if (flow->action.write.group_id) { + naction->has_group_id = true; + naction->group_id = flow->action.write.group_id; + } + + if (flow->action.apply.new_vlan_id) { + naction->has_new_vlan_id = true; + naction->new_vlan_id = flow->action.apply.new_vlan_id; + } + + new->next = flow_context->list; + flow_context->list = new; +} + +RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name, + bool has_tbl_id, + uint32_t tbl_id, + Error **errp) +{ + struct rocker *r; + struct world *w; + struct of_dpa *of_dpa; + struct of_dpa_flow_fill_context fill_context = { + .list = NULL, + .tbl_id = tbl_id, + }; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); + if (!w) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s doesn't have OF-DPA world", name); + return NULL; + } + + of_dpa = world_private(w); + + g_hash_table_foreach(of_dpa->flow_tbl, of_dpa_flow_fill, &fill_context); + + return fill_context.list; +} + +struct of_dpa_group_fill_context { + RockerOfDpaGroupList *list; + uint8_t type; +}; + +static void of_dpa_group_fill(void *key, void *value, void *user_data) +{ + struct of_dpa_group *group = value; + struct of_dpa_group_fill_context *flow_context = user_data; + RockerOfDpaGroupList *new; + RockerOfDpaGroup *ngroup; + struct uint32List *id; + int i; + + if (flow_context->type != 9 && + flow_context->type != ROCKER_GROUP_TYPE_GET(group->id)) { + return; + } + + new = g_malloc0(sizeof(*new)); + ngroup = new->value = g_malloc0(sizeof(*ngroup)); + + ngroup->id = group->id; + + ngroup->type = ROCKER_GROUP_TYPE_GET(group->id); + + switch (ngroup->type) { + case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE: + ngroup->has_vlan_id = true; + ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id); + ngroup->has_pport = true; + ngroup->pport = ROCKER_GROUP_PORT_GET(group->id); + ngroup->has_out_pport = true; + ngroup->out_pport = group->l2_interface.out_pport; + ngroup->has_pop_vlan = true; + ngroup->pop_vlan = group->l2_interface.pop_vlan; + break; + case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE: + ngroup->has_index = true; + ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id); + ngroup->has_group_id = true; + ngroup->group_id = group->l2_rewrite.group_id; + if (group->l2_rewrite.vlan_id) { + ngroup->has_set_vlan_id = true; + ngroup->set_vlan_id = ntohs(group->l2_rewrite.vlan_id); + } + break; + if (memcmp(group->l2_rewrite.src_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_src = true; + ngroup->set_eth_src = + qemu_mac_strdup_printf(group->l2_rewrite.src_mac.a); + } + if (memcmp(group->l2_rewrite.dst_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_dst = true; + ngroup->set_eth_dst = + qemu_mac_strdup_printf(group->l2_rewrite.dst_mac.a); + } + case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD: + case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST: + ngroup->has_vlan_id = true; + ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id); + ngroup->has_index = true; + ngroup->index = ROCKER_GROUP_INDEX_GET(group->id); + for (i = 0; i < group->l2_flood.group_count; i++) { + ngroup->has_group_ids = true; + id = g_malloc0(sizeof(*id)); + id->value = group->l2_flood.group_ids[i]; + id->next = ngroup->group_ids; + ngroup->group_ids = id; + } + break; + case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST: + ngroup->has_index = true; + ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id); + ngroup->has_group_id = true; + ngroup->group_id = group->l3_unicast.group_id; + if (group->l3_unicast.vlan_id) { + ngroup->has_set_vlan_id = true; + ngroup->set_vlan_id = ntohs(group->l3_unicast.vlan_id); + } + if (memcmp(group->l3_unicast.src_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_src = true; + ngroup->set_eth_src = + qemu_mac_strdup_printf(group->l3_unicast.src_mac.a); + } + if (memcmp(group->l3_unicast.dst_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_dst = true; + ngroup->set_eth_dst = + qemu_mac_strdup_printf(group->l3_unicast.dst_mac.a); + } + if (group->l3_unicast.ttl_check) { + ngroup->has_ttl_check = true; + ngroup->ttl_check = group->l3_unicast.ttl_check; + } + break; + } + + new->next = flow_context->list; + flow_context->list = new; +} + +RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, + bool has_type, + uint8_t type, + Error **errp) +{ + struct rocker *r; + struct world *w; + struct of_dpa *of_dpa; + struct of_dpa_group_fill_context fill_context = { + .list = NULL, + .type = type, + }; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); + if (!w) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s doesn't have OF-DPA world", name); + return NULL; + } + + of_dpa = world_private(w); + + g_hash_table_foreach(of_dpa->group_tbl, of_dpa_group_fill, &fill_context); + + return fill_context.list; +} + static WorldOps of_dpa_ops = { .init = of_dpa_init, .uninit = of_dpa_uninit, diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index f868108..e0db472 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -3240,6 +3240,7 @@ static const VMStateDescription vmstate_rtl8139_hotplug_ready ={ .name = "rtl8139/hotplug_ready", .version_id = 1, .minimum_version_id = 1, + .needed = rtl8139_hotplug_ready_needed, .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() } @@ -3335,13 +3336,9 @@ static const VMStateDescription vmstate_rtl8139 = { VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_rtl8139_hotplug_ready, - .needed = rtl8139_hotplug_ready_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_rtl8139_hotplug_ready, + NULL } }; diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index dfb328d..8bcdf3e 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2226,6 +2226,7 @@ static const VMStateDescription vmxstate_vmxnet3_mcast_list = { .version_id = 1, .minimum_version_id = 1, .pre_load = vmxnet3_mcast_list_pre_load, + .needed = vmxnet3_mc_list_needed, .fields = (VMStateField[]) { VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0, mcast_list_buff_size), @@ -2470,14 +2471,9 @@ static const VMStateDescription vmstate_vmxnet3 = { VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmxstate_vmxnet3_mcast_list, - .needed = vmxnet3_mc_list_needed - }, - { - /* empty element. */ - } + .subsections = (const VMStateDescription*[]) { + &vmxstate_vmxnet3_mcast_list, + NULL } }; diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index f1712b8..ed2424c 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -582,6 +582,7 @@ static const VMStateDescription vmstate_piix3_rcr = { .name = "PIIX3/rcr", .version_id = 1, .minimum_version_id = 1, + .needed = piix3_rcr_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(rcr, PIIX3State), VMSTATE_END_OF_LIST() @@ -600,12 +601,9 @@ static const VMStateDescription vmstate_piix3 = { PIIX_NUM_PIRQS, 3), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_piix3_rcr, - .needed = piix3_rcr_needed, - }, - { 0 } + .subsections = (const VMStateDescription*[]) { + &vmstate_piix3_rcr, + NULL } }; diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index bd2c0e4..f50b2f0 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1968,6 +1968,7 @@ static const VMStateDescription vmstate_scsi_sense_state = { .name = "SCSIDevice/sense", .version_id = 1, .minimum_version_id = 1, + .needed = scsi_sense_state_needed, .fields = (VMStateField[]) { VMSTATE_UINT8_SUB_ARRAY(sense, SCSIDevice, SCSI_SENSE_BUF_SIZE_OLD, @@ -1998,13 +1999,9 @@ const VMStateDescription vmstate_scsi_device = { }, VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_scsi_sense_state, - .needed = scsi_sense_state_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_scsi_sense_state, + NULL } }; diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c index ac3ab39..d1fe6d5 100644 --- a/hw/sd/pxa2xx_mmci.c +++ b/hw/sd/pxa2xx_mmci.c @@ -48,7 +48,6 @@ struct PXA2xxMMCIState { int resp_len; int cmdreq; - int ac_width; }; #define MMC_STRPCL 0x00 /* MMC Clock Start/Stop register */ @@ -215,7 +214,7 @@ static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s) pxa2xx_mmci_fifo_update(s); } -static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset) +static uint64_t pxa2xx_mmci_read(void *opaque, hwaddr offset, unsigned size) { PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; uint32_t ret; @@ -257,8 +256,8 @@ static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset) return 0; case MMC_RXFIFO: ret = 0; - while (s->ac_width -- && s->rx_len) { - ret |= s->rx_fifo[s->rx_start ++] << (s->ac_width << 3); + while (size-- && s->rx_len) { + ret |= s->rx_fifo[s->rx_start++] << (size << 3); s->rx_start &= 0x1f; s->rx_len --; } @@ -277,7 +276,7 @@ static uint32_t pxa2xx_mmci_read(void *opaque, hwaddr offset) } static void pxa2xx_mmci_write(void *opaque, - hwaddr offset, uint32_t value) + hwaddr offset, uint64_t value, unsigned size) { PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; @@ -370,9 +369,9 @@ static void pxa2xx_mmci_write(void *opaque, break; case MMC_TXFIFO: - while (s->ac_width -- && s->tx_len < 0x20) + while (size-- && s->tx_len < 0x20) s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] = - (value >> (s->ac_width << 3)) & 0xff; + (value >> (size << 3)) & 0xff; s->intreq &= ~INT_TXFIFO_REQ; pxa2xx_mmci_fifo_update(s); break; @@ -386,60 +385,9 @@ static void pxa2xx_mmci_write(void *opaque, } } -static uint32_t pxa2xx_mmci_readb(void *opaque, hwaddr offset) -{ - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - s->ac_width = 1; - return pxa2xx_mmci_read(opaque, offset); -} - -static uint32_t pxa2xx_mmci_readh(void *opaque, hwaddr offset) -{ - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - s->ac_width = 2; - return pxa2xx_mmci_read(opaque, offset); -} - -static uint32_t pxa2xx_mmci_readw(void *opaque, hwaddr offset) -{ - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - s->ac_width = 4; - return pxa2xx_mmci_read(opaque, offset); -} - -static void pxa2xx_mmci_writeb(void *opaque, - hwaddr offset, uint32_t value) -{ - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - s->ac_width = 1; - pxa2xx_mmci_write(opaque, offset, value); -} - -static void pxa2xx_mmci_writeh(void *opaque, - hwaddr offset, uint32_t value) -{ - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - s->ac_width = 2; - pxa2xx_mmci_write(opaque, offset, value); -} - -static void pxa2xx_mmci_writew(void *opaque, - hwaddr offset, uint32_t value) -{ - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - s->ac_width = 4; - pxa2xx_mmci_write(opaque, offset, value); -} - static const MemoryRegionOps pxa2xx_mmci_ops = { - .old_mmio = { - .read = { pxa2xx_mmci_readb, - pxa2xx_mmci_readh, - pxa2xx_mmci_readw, }, - .write = { pxa2xx_mmci_writeb, - pxa2xx_mmci_writeh, - pxa2xx_mmci_writew, }, - }, + .read = pxa2xx_mmci_read, + .write = pxa2xx_mmci_write, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 4221060..5e22ed7 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -127,7 +127,7 @@ static void r2d_fpga_irq_set(void *opaque, int n, int level) update_irl(fpga); } -static uint32_t r2d_fpga_read(void *opaque, hwaddr addr) +static uint64_t r2d_fpga_read(void *opaque, hwaddr addr, unsigned int size) { r2d_fpga_t *s = opaque; @@ -146,7 +146,7 @@ static uint32_t r2d_fpga_read(void *opaque, hwaddr addr) } static void -r2d_fpga_write(void *opaque, hwaddr addr, uint32_t value) +r2d_fpga_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) { r2d_fpga_t *s = opaque; @@ -170,10 +170,10 @@ r2d_fpga_write(void *opaque, hwaddr addr, uint32_t value) } static const MemoryRegionOps r2d_fpga_ops = { - .old_mmio = { - .read = { r2d_fpga_read, r2d_fpga_read, NULL, }, - .write = { r2d_fpga_write, r2d_fpga_write, NULL, }, - }, + .read = r2d_fpga_read, + .write = r2d_fpga_write, + .impl.min_access_size = 2, + .impl.max_access_size = 2, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index b6b8a20..b50071e 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -283,6 +283,7 @@ static const VMStateDescription vmstate_hpet_rtc_irq_level = { .name = "hpet/rtc_irq_level", .version_id = 1, .minimum_version_id = 1, + .needed = hpet_rtc_irq_level_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(rtc_irq_level, HPETState), VMSTATE_END_OF_LIST() @@ -322,13 +323,9 @@ static const VMStateDescription vmstate_hpet = { vmstate_hpet_timer, HPETTimer), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_hpet_rtc_irq_level, - .needed = hpet_rtc_irq_level_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_hpet_rtc_irq_level, + NULL } }; diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index f2b77fa..3204825 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -733,22 +733,23 @@ static int rtc_post_load(void *opaque, int version_id) return 0; } +static bool rtc_irq_reinject_on_ack_count_needed(void *opaque) +{ + RTCState *s = (RTCState *)opaque; + return s->irq_reinject_on_ack_count != 0; +} + static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = { .name = "mc146818rtc/irq_reinject_on_ack_count", .version_id = 1, .minimum_version_id = 1, + .needed = rtc_irq_reinject_on_ack_count_needed, .fields = (VMStateField[]) { VMSTATE_UINT16(irq_reinject_on_ack_count, RTCState), VMSTATE_END_OF_LIST() } }; -static bool rtc_irq_reinject_on_ack_count_needed(void *opaque) -{ - RTCState *s = (RTCState *)opaque; - return s->irq_reinject_on_ack_count != 0; -} - static const VMStateDescription vmstate_rtc = { .name = "mc146818rtc", .version_id = 3, @@ -770,13 +771,9 @@ static const VMStateDescription vmstate_rtc = { VMSTATE_UINT64_V(next_alarm_time, RTCState, 3), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_rtc_irq_reinject_on_ack_count, - .needed = rtc_irq_reinject_on_ack_count_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_rtc_irq_reinject_on_ack_count, + NULL } }; diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 1a22c9c..7d65818 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -2034,6 +2034,7 @@ static const VMStateDescription vmstate_ohci_eof_timer = { .version_id = 1, .minimum_version_id = 1, .pre_load = ohci_eof_timer_pre_load, + .needed = ohci_eof_timer_needed, .fields = (VMStateField[]) { VMSTATE_TIMER_PTR(eof_timer, OHCIState), VMSTATE_END_OF_LIST() @@ -2081,13 +2082,9 @@ static const VMStateDescription vmstate_ohci_state = { VMSTATE_BOOL(async_complete, OHCIState), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_ohci_eof_timer, - .needed = ohci_eof_timer_needed, - } , { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_ohci_eof_timer, + NULL } }; diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 242a654..6b4218c 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -2257,40 +2257,42 @@ static const VMStateInfo usbredir_ep_bufpq_vmstate_info = { /* For endp_data migration */ +static bool usbredir_bulk_receiving_needed(void *priv) +{ + struct endp_data *endp = priv; + + return endp->bulk_receiving_started; +} + static const VMStateDescription usbredir_bulk_receiving_vmstate = { .name = "usb-redir-ep/bulk-receiving", .version_id = 1, .minimum_version_id = 1, + .needed = usbredir_bulk_receiving_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(bulk_receiving_started, struct endp_data), VMSTATE_END_OF_LIST() } }; -static bool usbredir_bulk_receiving_needed(void *priv) +static bool usbredir_stream_needed(void *priv) { struct endp_data *endp = priv; - return endp->bulk_receiving_started; + return endp->max_streams; } static const VMStateDescription usbredir_stream_vmstate = { .name = "usb-redir-ep/stream-state", .version_id = 1, .minimum_version_id = 1, + .needed = usbredir_stream_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(max_streams, struct endp_data), VMSTATE_END_OF_LIST() } }; -static bool usbredir_stream_needed(void *priv) -{ - struct endp_data *endp = priv; - - return endp->max_streams; -} - static const VMStateDescription usbredir_ep_vmstate = { .name = "usb-redir-ep", .version_id = 1, @@ -2318,16 +2320,10 @@ static const VMStateDescription usbredir_ep_vmstate = { VMSTATE_INT32(bufpq_target_size, struct endp_data), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &usbredir_bulk_receiving_vmstate, - .needed = usbredir_bulk_receiving_needed, - }, { - .vmsd = &usbredir_stream_vmstate, - .needed = usbredir_stream_needed, - }, { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &usbredir_bulk_receiving_vmstate, + &usbredir_stream_vmstate, + NULL } }; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index fb49ffc..ee4e07c 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1053,6 +1053,7 @@ static const VMStateDescription vmstate_virtio_device_endian = { .name = "virtio/device_endian", .version_id = 1, .minimum_version_id = 1, + .needed = &virtio_device_endian_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(device_endian, VirtIODevice), VMSTATE_END_OF_LIST() @@ -1063,6 +1064,7 @@ static const VMStateDescription vmstate_virtio_64bit_features = { .name = "virtio/64bit_features", .version_id = 1, .minimum_version_id = 1, + .needed = &virtio_64bit_features_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(guest_features, VirtIODevice), VMSTATE_END_OF_LIST() @@ -1077,16 +1079,10 @@ static const VMStateDescription vmstate_virtio = { .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_virtio_device_endian, - .needed = &virtio_device_endian_needed - }, - { - .vmsd = &vmstate_virtio_64bit_features, - .needed = &virtio_64bit_features_needed - }, - { 0 } + .subsections = (const VMStateDescription*[]) { + &vmstate_virtio_device_endian, + &vmstate_virtio_64bit_features, + NULL } }; diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs index 4b0374a..72e3ffd 100644 --- a/hw/watchdog/Makefile.objs +++ b/hw/watchdog/Makefile.objs @@ -1,3 +1,4 @@ common-obj-y += watchdog.o common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o +common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index 54440c9..8d4b0ee 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -27,6 +27,7 @@ #include "sysemu/sysemu.h" #include "sysemu/watchdog.h" #include "qapi-event.h" +#include "hw/nmi.h" /* Possible values for action parameter. */ #define WDT_RESET 1 /* Hard reset. */ @@ -35,6 +36,7 @@ #define WDT_PAUSE 4 /* Pause. */ #define WDT_DEBUG 5 /* Prints a message and continues running. */ #define WDT_NONE 6 /* Do nothing. */ +#define WDT_NMI 7 /* Inject nmi into the guest */ static int watchdog_action = WDT_RESET; static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list; @@ -95,6 +97,8 @@ int select_watchdog_action(const char *p) watchdog_action = WDT_DEBUG; else if (strcasecmp(p, "none") == 0) watchdog_action = WDT_NONE; + else if (strcasecmp(p, "inject-nmi") == 0) + watchdog_action = WDT_NMI; else return -1; @@ -138,5 +142,11 @@ void watchdog_perform_action(void) case WDT_NONE: qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_NONE, &error_abort); break; + + case WDT_NMI: + qapi_event_send_watchdog(WATCHDOG_EXPIRATION_ACTION_INJECT_NMI, + &error_abort); + inject_nmi(); + break; } } diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c new file mode 100644 index 0000000..1185e06 --- /dev/null +++ b/hw/watchdog/wdt_diag288.c @@ -0,0 +1,122 @@ +/* + * watchdog device diag288 support + * + * Copyright IBM, Corp. 2015 + * + * Authors: + * Xu Wang <gesaint@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#include "sysemu/watchdog.h" +#include "hw/sysbus.h" +#include "qemu/timer.h" +#include "hw/watchdog/wdt_diag288.h" + +static WatchdogTimerModel model = { + .wdt_name = TYPE_WDT_DIAG288, + .wdt_description = "diag288 device for s390x platform", +}; + +static const VMStateDescription vmstate_diag288 = { + .name = "vmstate_diag288", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_TIMER_PTR(timer, DIAG288State), + VMSTATE_BOOL(enabled, DIAG288State), + VMSTATE_END_OF_LIST() + } +}; + +static void wdt_diag288_reset(DeviceState *dev) +{ + DIAG288State *diag288 = DIAG288(dev); + + diag288->enabled = false; + timer_del(diag288->timer); +} + +static void diag288_timer_expired(void *dev) +{ + qemu_log_mask(CPU_LOG_RESET, "Watchdog timer expired.\n"); + watchdog_perform_action(); + wdt_diag288_reset(dev); +} + +static int wdt_diag288_handle_timer(DIAG288State *diag288, + uint64_t func, uint64_t timeout) +{ + switch (func) { + case WDT_DIAG288_INIT: + diag288->enabled = true; + /* fall through */ + case WDT_DIAG288_CHANGE: + if (!diag288->enabled) { + return -1; + } + timer_mod(diag288->timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + timeout * get_ticks_per_sec()); + break; + case WDT_DIAG288_CANCEL: + if (!diag288->enabled) { + return -1; + } + diag288->enabled = false; + timer_del(diag288->timer); + break; + default: + return -1; + } + + return 0; +} + +static void wdt_diag288_realize(DeviceState *dev, Error **errp) +{ + DIAG288State *diag288 = DIAG288(dev); + + diag288->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, diag288_timer_expired, + dev); +} + +static void wdt_diag288_unrealize(DeviceState *dev, Error **errp) +{ + DIAG288State *diag288 = DIAG288(dev); + + timer_del(diag288->timer); + timer_free(diag288->timer); +} + +static void wdt_diag288_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + DIAG288Class *diag288 = DIAG288_CLASS(klass); + + dc->realize = wdt_diag288_realize; + dc->unrealize = wdt_diag288_unrealize; + dc->reset = wdt_diag288_reset; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->vmsd = &vmstate_diag288; + diag288->handle_timer = wdt_diag288_handle_timer; +} + +static const TypeInfo wdt_diag288_info = { + .class_init = wdt_diag288_class_init, + .parent = TYPE_DEVICE, + .name = TYPE_WDT_DIAG288, + .instance_size = sizeof(DIAG288State), + .class_size = sizeof(DIAG288Class), +}; + +static void wdt_diag288_register_types(void) +{ + watchdog_add_model(&model); + type_register_static(&wdt_diag288_info); +} + +type_init(wdt_diag288_register_types) diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index b2cb22b..2510e2e 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -714,9 +714,7 @@ int xen_be_init(void) return -1; } - if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) { - goto err; - } + qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL); if (xen_xc == XC_HANDLER_INITIAL_VALUE) { /* Check if xen_init() have been called */ diff --git a/include/block/aio.h b/include/block/aio.h index d2bb423..b46103e 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -241,7 +241,7 @@ bool aio_dispatch(AioContext *ctx); bool aio_poll(AioContext *ctx, bool blocking); /* Register a file descriptor and associated callbacks. Behaves very similarly - * to qemu_set_fd_handler2. Unlike qemu_set_fd_handler2, these callbacks will + * to qemu_set_fd_handler. Unlike qemu_set_fd_handler, these callbacks will * be invoked when using aio_poll(). * * Code that invokes AIO completion functions should rely on this function diff --git a/include/block/block.h b/include/block/block.h index f7680b6..07bb724 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -12,6 +12,7 @@ /* block.c */ typedef struct BlockDriver BlockDriver; typedef struct BlockJob BlockJob; +typedef struct BdrvChildRole BdrvChildRole; typedef struct BlockDriverInfo { /* in bytes, 0 if irrelevant */ @@ -90,6 +91,14 @@ typedef struct HDGeometry { #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH) + +/* Option names of options parsed by the block layer */ + +#define BDRV_OPT_CACHE_WB "cache.writeback" +#define BDRV_OPT_CACHE_DIRECT "cache.direct" +#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush" + + #define BDRV_SECTOR_BITS 9 #define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS) #define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1) @@ -173,8 +182,9 @@ void bdrv_stats_print(Monitor *mon, const QObject *data); void bdrv_info_stats(Monitor *mon, QObject **ret_data); /* disk I/O throttling */ -void bdrv_io_limits_enable(BlockDriverState *bs); +void bdrv_io_limits_enable(BlockDriverState *bs, const char *group); void bdrv_io_limits_disable(BlockDriverState *bs); +void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group); void bdrv_init(void); void bdrv_init_with_whitelist(void); @@ -195,7 +205,8 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top); int bdrv_parse_cache_flags(const char *mode, int *flags); int bdrv_parse_discard_flags(const char *mode, int *flags); int bdrv_open_image(BlockDriverState **pbs, const char *filename, - QDict *options, const char *bdref_key, int flags, + QDict *options, const char *bdref_key, + BlockDriverState* parent, const BdrvChildRole *child_role, bool allow_none, Error **errp); void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd); int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp); diff --git a/include/block/block_int.h b/include/block/block_int.h index f004378..888ec09 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -330,6 +330,19 @@ typedef struct BdrvAioNotifier { QLIST_ENTRY(BdrvAioNotifier) list; } BdrvAioNotifier; +struct BdrvChildRole { + int (*inherit_flags)(int parent_flags); +}; + +extern const BdrvChildRole child_file; +extern const BdrvChildRole child_format; + +typedef struct BdrvChild { + BlockDriverState *bs; + const BdrvChildRole *role; + QLIST_ENTRY(BdrvChild) next; +} BdrvChild; + /* * Note: the function bdrv_append() copies and swaps contents of * BlockDriverStates, so if you add new fields to this struct, please @@ -379,9 +392,14 @@ struct BlockDriverState { unsigned int serialising_in_flight; /* I/O throttling */ - ThrottleState throttle_state; CoQueue throttled_reqs[2]; bool io_limits_enabled; + /* The following fields are protected by the ThrottleGroup lock. + * See the ThrottleGroup documentation for details. */ + ThrottleState *throttle_state; + ThrottleTimers throttle_timers; + unsigned pending_reqs[2]; + QLIST_ENTRY(BlockDriverState) round_robin; /* I/O stats (display with "info blockstats"). */ BlockAcctStats stats; @@ -424,6 +442,12 @@ struct BlockDriverState { /* long-running background operation */ BlockJob *job; + /* The node that this node inherited default options from (and a reopen on + * which can affect this node by changing these defaults). This is always a + * parent node of this node. */ + BlockDriverState *inherits_from; + QLIST_HEAD(, BdrvChild) children; + QDict *options; BlockdevDetectZeroesOptions detect_zeroes; diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h new file mode 100644 index 0000000..fab113f --- /dev/null +++ b/include/block/throttle-groups.h @@ -0,0 +1,46 @@ +/* + * QEMU block throttling group infrastructure + * + * Copyright (C) Nodalink, EURL. 2014 + * Copyright (C) Igalia, S.L. 2015 + * + * Authors: + * Benoît Canet <benoit.canet@nodalink.com> + * Alberto Garcia <berto@igalia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef THROTTLE_GROUPS_H +#define THROTTLE_GROUPS_H + +#include "qemu/throttle.h" +#include "block/block_int.h" + +const char *throttle_group_get_name(BlockDriverState *bs); + +void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg); +void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg); + +void throttle_group_register_bs(BlockDriverState *bs, const char *groupname); +void throttle_group_unregister_bs(BlockDriverState *bs); + +void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, + unsigned int bytes, + bool is_write); + +void throttle_group_lock(BlockDriverState *bs); +void throttle_group_unlock(BlockDriverState *bs); + +#endif diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 43428bd..de8a720 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -126,10 +126,10 @@ void cpu_flush_icache_range(hwaddr start, int len); extern struct MemoryRegion io_mem_rom; extern struct MemoryRegion io_mem_notdirty; -typedef void (RAMBlockIterFunc)(void *host_addr, +typedef int (RAMBlockIterFunc)(const char *block_name, void *host_addr, ram_addr_t offset, ram_addr_t length, void *opaque); -void qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); +int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 2f7a4f1..2573e8c 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -105,6 +105,8 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, hwaddr paddr, MemTxAttrs attrs, int prot, int mmu_idx, target_ulong size); void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr); +void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx, + uintptr_t retaddr); #else static inline void tlb_flush_page(CPUState *cpu, target_ulong addr) { diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h index 87025c3..3b67462 100644 --- a/include/exec/user/thunk.h +++ b/include/exec/user/thunk.h @@ -74,7 +74,7 @@ const argtype *thunk_convert(void *dst, const void *src, const argtype *type_ptr, int to_host); #ifndef NO_THUNK_TYPE_SIZE -extern StructEntry struct_entries[]; +extern StructEntry *struct_entries; int thunk_type_size_array(const argtype *type_ptr, int is_host); int thunk_type_align_array(const argtype *type_ptr, int is_host); @@ -186,4 +186,6 @@ unsigned int target_to_host_bitmask(unsigned int x86_mask, unsigned int host_to_target_bitmask(unsigned int alpha_mask, const bitmask_transtbl * trans_tbl); +void thunk_init(unsigned int max_structs); + #endif diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 59cf277..7b4bfb7 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -197,6 +197,38 @@ enum { }; /* + * Serial Port Console Redirection Table (SPCR), Rev. 1.02 + * + * For .interface_type see Debug Port Table 2 (DBG2) serial port + * subtypes in Table 3, Rev. May 22, 2012 + */ +struct AcpiSerialPortConsoleRedirection { + ACPI_TABLE_HEADER_DEF + uint8_t interface_type; + uint8_t reserved1[3]; + struct AcpiGenericAddress base_address; + uint8_t interrupt_types; + uint8_t irq; + uint32_t gsi; + uint8_t baud; + uint8_t parity; + uint8_t stopbits; + uint8_t flowctrl; + uint8_t term_type; + uint8_t reserved2; + uint16_t pci_device_id; + uint16_t pci_vendor_id; + uint8_t pci_bus; + uint8_t pci_slot; + uint8_t pci_func; + uint32_t pci_flags; + uint8_t pci_seg; + uint32_t reserved3; +} QEMU_PACKED; +typedef struct AcpiSerialPortConsoleRedirection + AcpiSerialPortConsoleRedirection; + +/* * ACPI 1.0 Root System Description Table (RSDT) */ struct AcpiRsdtDescriptorRev1 diff --git a/include/hw/mips/mips.h b/include/hw/mips/mips.h index 2a7a9c9..e0065ce 100644 --- a/include/hw/mips/mips.h +++ b/include/hw/mips/mips.h @@ -15,18 +15,9 @@ PCIBus *bonito_init(qemu_irq *pic); /* rc4030.c */ typedef struct rc4030DMAState *rc4030_dma; -void rc4030_dma_memory_rw(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write); void rc4030_dma_read(void *dma, uint8_t *buf, int len); void rc4030_dma_write(void *dma, uint8_t *buf, int len); -void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus, - qemu_irq **irqs, rc4030_dma **dmas, - MemoryRegion *sysmem); - -/* dp8393x.c */ -void dp83932_init(NICInfo *nd, hwaddr base, int it_shift, - MemoryRegion *address_space, - qemu_irq irq, void* mem_opaque, - void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write)); +DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr); #endif diff --git a/include/hw/nmi.h b/include/hw/nmi.h index b541772..f4cec62 100644 --- a/include/hw/nmi.h +++ b/include/hw/nmi.h @@ -45,5 +45,6 @@ typedef struct NMIClass { } NMIClass; void nmi_monitor_handle(int cpu_index, Error **errp); +void inject_nmi(void); #endif /* NMI_H */ diff --git a/include/hw/watchdog/wdt_diag288.h b/include/hw/watchdog/wdt_diag288.h new file mode 100644 index 0000000..7f3fd45 --- /dev/null +++ b/include/hw/watchdog/wdt_diag288.h @@ -0,0 +1,36 @@ +#ifndef WDT_DIAG288_H +#define WDT_DIAG288_H + +#include "hw/qdev.h" + +#define TYPE_WDT_DIAG288 "diag288" +#define DIAG288(obj) \ + OBJECT_CHECK(DIAG288State, (obj), TYPE_WDT_DIAG288) +#define DIAG288_CLASS(klass) \ + OBJECT_CLASS_CHECK(DIAG288Class, (klass), TYPE_WDT_DIAG288) +#define DIAG288_GET_CLASS(obj) \ + OBJECT_GET_CLASS(DIAG288Class, (obj), TYPE_WDT_DIAG288) + +#define WDT_DIAG288_INIT 0 +#define WDT_DIAG288_CHANGE 1 +#define WDT_DIAG288_CANCEL 2 + +typedef struct DIAG288State { + /*< private >*/ + DeviceState parent_obj; + QEMUTimer *timer; + bool enabled; + + /*< public >*/ +} DIAG288State; + +typedef struct DIAG288Class { + /*< private >*/ + DeviceClass parent_class; + + /*< public >*/ + int (*handle_timer)(DIAG288State *dev, + uint64_t func, uint64_t timeout); +} DIAG288Class; + +#endif /* WDT_DIAG288_H */ diff --git a/include/migration/migration.h b/include/migration/migration.h index a6e025a..9387c8c 100644 --- a/include/migration/migration.h +++ b/include/migration/migration.h @@ -34,6 +34,7 @@ #define QEMU_VM_SECTION_FULL 0x04 #define QEMU_VM_SUBSECTION 0x05 #define QEMU_VM_VMDESCRIPTION 0x06 +#define QEMU_VM_SECTION_FOOTER 0x7e struct MigrationParams { bool blk; @@ -42,6 +43,20 @@ struct MigrationParams { typedef struct MigrationState MigrationState; +typedef QLIST_HEAD(, LoadStateEntry) LoadStateEntry_Head; + +/* State for the incoming migration */ +struct MigrationIncomingState { + QEMUFile *file; + + /* See savevm.c */ + LoadStateEntry_Head loadvm_handlers; +}; + +MigrationIncomingState *migration_incoming_get_current(void); +MigrationIncomingState *migration_incoming_state_new(QEMUFile *f); +void migration_incoming_state_destroy(void); + struct MigrationState { int64_t bandwidth_limit; @@ -180,4 +195,6 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, ram_addr_t offset, size_t size, uint64_t *bytes_sent); +void ram_mig_init(void); +void savevm_skip_section_footers(void); #endif diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h index a01c5b8..4f67d79 100644 --- a/include/migration/qemu-file.h +++ b/include/migration/qemu-file.h @@ -157,7 +157,7 @@ static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v) void qemu_put_be16(QEMUFile *f, unsigned int v); void qemu_put_be32(QEMUFile *f, unsigned int v); void qemu_put_be64(QEMUFile *f, uint64_t v); -int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset); +int qemu_peek_buffer(QEMUFile *f, uint8_t **buf, int size, size_t offset); int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size); ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, int level); @@ -312,4 +312,7 @@ static inline void qemu_get_sbe64s(QEMUFile *f, int64_t *pv) { qemu_get_be64s(f, (uint64_t *)pv); } + +size_t qemu_get_counted_string(QEMUFile *f, char buf[256]); + #endif diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index bc7616a..7153b1e 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -120,11 +120,6 @@ typedef struct { bool (*field_exists)(void *opaque, int version_id); } VMStateField; -typedef struct VMStateSubsection { - const VMStateDescription *vmsd; - bool (*needed)(void *opaque); -} VMStateSubsection; - struct VMStateDescription { const char *name; int unmigratable; @@ -135,8 +130,9 @@ struct VMStateDescription { int (*pre_load)(void *opaque); int (*post_load)(void *opaque, int version_id); void (*pre_save)(void *opaque); + bool (*needed)(void *opaque); VMStateField *fields; - const VMStateSubsection *subsections; + const VMStateDescription **subsections; }; extern const VMStateDescription vmstate_dummy; @@ -812,6 +808,8 @@ extern const VMStateInfo vmstate_info_bitmap; #define SELF_ANNOUNCE_ROUNDS 5 +void loadvm_free_handlers(MigrationIncomingState *mis); + int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id); void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 57f8394..88644ce 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -8,7 +8,6 @@ #include "qemu/readline.h" extern Monitor *cur_mon; -extern Monitor *default_mon; /* flags for monitor_init */ #define MONITOR_IS_DEFAULT 0x01 diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index d68f4eb..9fbf68e 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -65,11 +65,15 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key, int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value); const char *qdict_get_try_str(const QDict *qdict, const char *key); +void qdict_copy_default(QDict *dst, QDict *src, const char *key); +void qdict_set_default_str(QDict *dst, const char *key, const char *val); + QDict *qdict_clone_shallow(const QDict *src); void qdict_flatten(QDict *qdict); void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); void qdict_array_split(QDict *src, QList **dst); +int qdict_array_entries(QDict *src, const char *subqdict); void qdict_join(QDict *dest, QDict *src, bool overwrite); diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 62c68c0..0f4a0fd 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -96,8 +96,7 @@ AioContext *qemu_get_aio_context(void); * that the main loop waits for. * * Calling qemu_notify_event is rarely necessary, because main loop - * services (bottom halves and timers) call it themselves. One notable - * exception occurs when using qemu_set_fd_handler2 (see below). + * services (bottom halves and timers) call it themselves. */ void qemu_notify_event(void); @@ -172,52 +171,6 @@ typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); typedef int IOCanReadHandler(void *opaque); /** - * qemu_set_fd_handler2: Register a file descriptor with the main loop - * - * This function tells the main loop to wake up whenever one of the - * following conditions is true: - * - * 1) if @fd_write is not %NULL, when the file descriptor is writable; - * - * 2) if @fd_read is not %NULL, when the file descriptor is readable. - * - * @fd_read_poll can be used to disable the @fd_read callback temporarily. - * This is useful to avoid calling qemu_set_fd_handler2 every time the - * client becomes interested in reading (or dually, stops being interested). - * A typical example is when @fd is a listening socket and you want to bound - * the number of active clients. Remember to call qemu_notify_event whenever - * the condition may change from %false to %true. - * - * The callbacks that are set up by qemu_set_fd_handler2 are level-triggered. - * If @fd_read does not read from @fd, or @fd_write does not write to @fd - * until its buffers are full, they will be called again on the next - * iteration. - * - * @fd: The file descriptor to be observed. Under Windows it must be - * a #SOCKET. - * - * @fd_read_poll: A function that returns 1 if the @fd_read callback - * should be fired. If the function returns 0, the main loop will not - * end its iteration even if @fd becomes readable. - * - * @fd_read: A level-triggered callback that is fired if @fd is readable - * at the beginning of a main loop iteration, or if it becomes readable - * during one. - * - * @fd_write: A level-triggered callback that is fired when @fd is writable - * at the beginning of a main loop iteration, or if it becomes writable - * during one. - * - * @opaque: A pointer-sized value that is passed to @fd_read_poll, - * @fd_read and @fd_write. - */ -int qemu_set_fd_handler2(int fd, - IOCanReadHandler *fd_read_poll, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque); - -/** * qemu_set_fd_handler: Register a file descriptor with the main loop * * This function tells the main loop to wake up whenever one of the @@ -245,10 +198,10 @@ int qemu_set_fd_handler2(int fd, * * @opaque: A pointer-sized value that is passed to @fd_read and @fd_write. */ -int qemu_set_fd_handler(int fd, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque); +void qemu_set_fd_handler(int fd, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque); #ifdef CONFIG_POSIX /** diff --git a/include/qemu/queue.h b/include/qemu/queue.h index f781aa2..a8d3cb8 100644 --- a/include/qemu/queue.h +++ b/include/qemu/queue.h @@ -117,6 +117,12 @@ struct { \ } \ } while (/*CONSTCOND*/0) +#define QLIST_FIX_HEAD_PTR(head, field) do { \ + if ((head)->lh_first != NULL) { \ + (head)->lh_first->field.le_prev = &(head)->lh_first; \ + } \ +} while (/*CONSTCOND*/0) + #define QLIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h index b890613..5af76f0 100644 --- a/include/qemu/throttle.h +++ b/include/qemu/throttle.h @@ -1,10 +1,12 @@ /* * QEMU throttling infrastructure * - * Copyright (C) Nodalink, SARL. 2013 + * Copyright (C) Nodalink, EURL. 2013-2014 + * Copyright (C) Igalia, S.L. 2015 * - * Author: - * Benoît Canet <benoit.canet@irqsave.net> + * Authors: + * Benoît Canet <benoit.canet@nodalink.com> + * Alberto Garcia <berto@igalia.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -65,14 +67,17 @@ typedef struct ThrottleConfig { typedef struct ThrottleState { ThrottleConfig cfg; /* configuration */ int64_t previous_leak; /* timestamp of the last leak done */ - QEMUTimer * timers[2]; /* timers used to do the throttling */ +} ThrottleState; + +typedef struct ThrottleTimers { + QEMUTimer *timers[2]; /* timers used to do the throttling */ QEMUClockType clock_type; /* the clock used */ /* Callbacks */ QEMUTimerCB *read_timer_cb; QEMUTimerCB *write_timer_cb; void *timer_opaque; -} ThrottleState; +} ThrottleTimers; /* operations on single leaky buckets */ void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta); @@ -86,20 +91,23 @@ bool throttle_compute_timer(ThrottleState *ts, int64_t *next_timestamp); /* init/destroy cycle */ -void throttle_init(ThrottleState *ts, - AioContext *aio_context, - QEMUClockType clock_type, - void (read_timer)(void *), - void (write_timer)(void *), - void *timer_opaque); +void throttle_init(ThrottleState *ts); + +void throttle_timers_init(ThrottleTimers *tt, + AioContext *aio_context, + QEMUClockType clock_type, + QEMUTimerCB *read_timer_cb, + QEMUTimerCB *write_timer_cb, + void *timer_opaque); -void throttle_destroy(ThrottleState *ts); +void throttle_timers_destroy(ThrottleTimers *tt); -void throttle_detach_aio_context(ThrottleState *ts); +void throttle_timers_detach_aio_context(ThrottleTimers *tt); -void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context); +void throttle_timers_attach_aio_context(ThrottleTimers *tt, + AioContext *new_context); -bool throttle_have_timer(ThrottleState *ts); +bool throttle_timers_are_initialized(ThrottleTimers *tt); /* configuration */ bool throttle_enabled(ThrottleConfig *cfg); @@ -108,12 +116,16 @@ bool throttle_conflicting(ThrottleConfig *cfg); bool throttle_is_valid(ThrottleConfig *cfg); -void throttle_config(ThrottleState *ts, ThrottleConfig *cfg); +void throttle_config(ThrottleState *ts, + ThrottleTimers *tt, + ThrottleConfig *cfg); void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg); /* usage */ -bool throttle_schedule_timer(ThrottleState *ts, bool is_write); +bool throttle_schedule_timer(ThrottleState *ts, + ThrottleTimers *tt, + bool is_write); void throttle_account(ThrottleState *ts, bool is_write, uint64_t size); diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index cde3314..6fdcbcd 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -31,6 +31,7 @@ typedef struct I2CBus I2CBus; typedef struct I2SCodec I2SCodec; typedef struct ISABus ISABus; typedef struct ISADevice ISADevice; +typedef struct LoadStateEntry LoadStateEntry; typedef struct MACAddr MACAddr; typedef struct MachineClass MachineClass; typedef struct MachineState MachineState; @@ -38,6 +39,7 @@ typedef struct MemoryListener MemoryListener; typedef struct MemoryMappingList MemoryMappingList; typedef struct MemoryRegion MemoryRegion; typedef struct MemoryRegionSection MemoryRegionSection; +typedef struct MigrationIncomingState MigrationIncomingState; typedef struct MigrationParams MigrationParams; typedef struct Monitor Monitor; typedef struct MouseTransformInfo MouseTransformInfo; diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 54b36c1..c38892f 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -30,7 +30,6 @@ extern const uint32_t arch_type; void select_soundhw(const char *optarg); void do_acpitable_option(const QemuOpts *opts); void do_smbios_option(QemuOpts *opts); -void ram_mig_init(void); void cpudef_init(void); void audio_init(void); int kvm_available(void); diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 7beb926..0304aa7 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -84,6 +84,7 @@ void qemu_announce_self(void); bool qemu_savevm_state_blocked(Error **errp); void qemu_savevm_state_begin(QEMUFile *f, const MigrationParams *params); +void qemu_savevm_state_header(QEMUFile *f); int qemu_savevm_state_iterate(QEMUFile *f); void qemu_savevm_state_complete(QEMUFile *f); void qemu_savevm_state_cancel(void); diff --git a/iohandler.c b/iohandler.c index cca614f..826f713 100644 --- a/iohandler.c +++ b/iohandler.c @@ -33,7 +33,6 @@ #endif typedef struct IOHandlerRecord { - IOCanReadHandler *fd_read_poll; IOHandler *fd_read; IOHandler *fd_write; void *opaque; @@ -46,11 +45,7 @@ typedef struct IOHandlerRecord { static QLIST_HEAD(, IOHandlerRecord) io_handlers = QLIST_HEAD_INITIALIZER(io_handlers); - -/* XXX: fd_read_poll should be suppressed, but an API change is - necessary in the character devices to suppress fd_can_read(). */ -int qemu_set_fd_handler2(int fd, - IOCanReadHandler *fd_read_poll, +void qemu_set_fd_handler(int fd, IOHandler *fd_read, IOHandler *fd_write, void *opaque) @@ -75,7 +70,6 @@ int qemu_set_fd_handler2(int fd, QLIST_INSERT_HEAD(&io_handlers, ioh, next); found: ioh->fd = fd; - ioh->fd_read_poll = fd_read_poll; ioh->fd_read = fd_read; ioh->fd_write = fd_write; ioh->opaque = opaque; @@ -83,15 +77,6 @@ int qemu_set_fd_handler2(int fd, ioh->deleted = 0; qemu_notify_event(); } - return 0; -} - -int qemu_set_fd_handler(int fd, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque) -{ - return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque); } void qemu_iohandler_fill(GArray *pollfds) @@ -103,9 +88,7 @@ void qemu_iohandler_fill(GArray *pollfds) if (ioh->deleted) continue; - if (ioh->fd_read && - (!ioh->fd_read_poll || - ioh->fd_read_poll(ioh->opaque) != 0)) { + if (ioh->fd_read) { events |= G_IO_IN | G_IO_HUP | G_IO_ERR; } if (ioh->fd_write) { @@ -31,14 +31,21 @@ typedef ObjectClass IOThreadClass; static void *iothread_run(void *opaque) { IOThread *iothread = opaque; + bool blocking; qemu_mutex_lock(&iothread->init_done_lock); iothread->thread_id = qemu_get_thread_id(); qemu_cond_signal(&iothread->init_done_cond); qemu_mutex_unlock(&iothread->init_done_lock); - while (!atomic_read(&iothread->stopping)) { - aio_poll(iothread->ctx, true); + while (!iothread->stopping) { + aio_context_acquire(iothread->ctx); + blocking = true; + while (!iothread->stopping && aio_poll(iothread->ctx, blocking)) { + /* Progress was made, keep going */ + blocking = false; + } + aio_context_release(iothread->ctx); } return NULL; } diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0ba9706..1788368 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1075,6 +1075,35 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 +enum { + SH_CPU_HAS_FPU = 0x0001, /* Hardware FPU support */ + SH_CPU_HAS_P2_FLUSH_BUG = 0x0002, /* Need to flush the cache in P2 area */ + SH_CPU_HAS_MMU_PAGE_ASSOC = 0x0004, /* SH3: TLB way selection bit support */ + SH_CPU_HAS_DSP = 0x0008, /* SH-DSP: DSP support */ + SH_CPU_HAS_PERF_COUNTER = 0x0010, /* Hardware performance counters */ + SH_CPU_HAS_PTEA = 0x0020, /* PTEA register */ + SH_CPU_HAS_LLSC = 0x0040, /* movli.l/movco.l */ + SH_CPU_HAS_L2_CACHE = 0x0080, /* Secondary cache / URAM */ + SH_CPU_HAS_OP32 = 0x0100, /* 32-bit instruction support */ + SH_CPU_HAS_PTEAEX = 0x0200, /* PTE ASID Extension support */ +}; + +#define ELF_HWCAP get_elf_hwcap() + +static uint32_t get_elf_hwcap(void) +{ + SuperHCPU *cpu = SUPERH_CPU(thread_cpu); + uint32_t hwcap = 0; + + hwcap |= SH_CPU_HAS_FPU; + + if (cpu->env.features & SH_FEATURE_SH4A) { + hwcap |= SH_CPU_HAS_LLSC; + } + + return hwcap; +} + #endif #ifdef TARGET_CRIS @@ -1227,7 +1256,8 @@ struct exec /* Necessary parameters */ #define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE -#define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1)) +#define TARGET_ELF_PAGESTART(_v) ((_v) & \ + ~(abi_ulong)(TARGET_ELF_EXEC_PAGESIZE-1)) #define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1)) #define DLINFO_ITEMS 14 diff --git a/linux-user/main.c b/linux-user/main.c index 6989b82..c855bcc 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3459,8 +3459,8 @@ CPUArchState *cpu_copy(CPUArchState *env) /* Clone all break/watchpoints. Note: Once we support ptrace with hw-debug register access, make sure BP_CPU break/watchpoints are handled correctly on clone. */ - QTAILQ_INIT(&cpu->breakpoints); - QTAILQ_INIT(&cpu->watchpoints); + QTAILQ_INIT(&new_cpu->breakpoints); + QTAILQ_INIT(&new_cpu->watchpoints); QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { cpu_breakpoint_insert(new_cpu, bp->pc, bp->flags, NULL); } @@ -3925,6 +3925,8 @@ int main(int argc, char **argv, char **envp) # else cpu_model = "750"; # endif +#elif defined TARGET_SH4 + cpu_model = TYPE_SH7785_CPU; #else cpu_model = "any"; #endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 1622ad6..f62c698 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1202,6 +1202,15 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, space += CMSG_SPACE(len); if (space > msgh->msg_controllen) { space -= CMSG_SPACE(len); + /* This is a QEMU bug, since we allocated the payload + * area ourselves (unlike overflow in host-to-target + * conversion, which is just the guest giving us a buffer + * that's too small). It can't happen for the payload types + * we currently support; if it becomes an issue in future + * we would need to improve our allocation strategy to + * something more intelligent than "twice the size of the + * target buffer we're reading from". + */ gemu_log("Host cmsg overflow\n"); break; } @@ -1219,17 +1228,18 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, int *target_fd = (int *)target_data; int i, numfds = len / sizeof(int); - for (i = 0; i < numfds; i++) - fd[i] = tswap32(target_fd[i]); + for (i = 0; i < numfds; i++) { + __get_user(fd[i], target_fd + i); + } } else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { struct ucred *cred = (struct ucred *)data; struct target_ucred *target_cred = (struct target_ucred *)target_data; - __put_user(target_cred->pid, &cred->pid); - __put_user(target_cred->uid, &cred->uid); - __put_user(target_cred->gid, &cred->gid); + __get_user(cred->pid, &target_cred->pid); + __get_user(cred->uid, &target_cred->uid); + __get_user(cred->gid, &target_cred->gid); } else { gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); @@ -1267,11 +1277,16 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, void *target_data = TARGET_CMSG_DATA(target_cmsg); int len = cmsg->cmsg_len - CMSG_ALIGN(sizeof (struct cmsghdr)); + int tgt_len, tgt_space; - space += TARGET_CMSG_SPACE(len); - if (space > msg_controllen) { - space -= TARGET_CMSG_SPACE(len); - gemu_log("Target cmsg overflow\n"); + /* We never copy a half-header but may copy half-data; + * this is Linux's behaviour in put_cmsg(). Note that + * truncation here is a guest problem (which we report + * to the guest via the CTRUNC bit), unlike truncation + * in target_to_host_cmsg, which is a QEMU bug. + */ + if (msg_controllen < sizeof(struct cmsghdr)) { + target_msgh->msg_flags |= tswap32(MSG_CTRUNC); break; } @@ -1281,8 +1296,35 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, target_cmsg->cmsg_level = tswap32(cmsg->cmsg_level); } target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type); - target_cmsg->cmsg_len = tswapal(TARGET_CMSG_LEN(len)); + tgt_len = TARGET_CMSG_LEN(len); + + /* Payload types which need a different size of payload on + * the target must adjust tgt_len here. + */ + switch (cmsg->cmsg_level) { + case SOL_SOCKET: + switch (cmsg->cmsg_type) { + case SO_TIMESTAMP: + tgt_len = sizeof(struct target_timeval); + break; + default: + break; + } + default: + break; + } + + if (msg_controllen < tgt_len) { + target_msgh->msg_flags |= tswap32(MSG_CTRUNC); + tgt_len = msg_controllen; + } + + /* We must now copy-and-convert len bytes of payload + * into tgt_len bytes of destination space. Bear in mind + * that in both source and destination we may be dealing + * with a truncated value! + */ switch (cmsg->cmsg_level) { case SOL_SOCKET: switch (cmsg->cmsg_type) { @@ -1290,10 +1332,11 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, { int *fd = (int *)data; int *target_fd = (int *)target_data; - int i, numfds = len / sizeof(int); + int i, numfds = tgt_len / sizeof(int); - for (i = 0; i < numfds; i++) - target_fd[i] = tswap32(fd[i]); + for (i = 0; i < numfds; i++) { + __put_user(fd[i], target_fd + i); + } break; } case SO_TIMESTAMP: @@ -1302,12 +1345,14 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, struct target_timeval *target_tv = (struct target_timeval *)target_data; - if (len != sizeof(struct timeval)) + if (len != sizeof(struct timeval) || + tgt_len != sizeof(struct target_timeval)) { goto unimplemented; + } /* copy struct timeval to target */ - target_tv->tv_sec = tswapal(tv->tv_sec); - target_tv->tv_usec = tswapal(tv->tv_usec); + __put_user(tv->tv_sec, &target_tv->tv_sec); + __put_user(tv->tv_usec, &target_tv->tv_usec); break; } case SCM_CREDENTIALS: @@ -1330,9 +1375,19 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, unimplemented: gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type); - memcpy(target_data, data, len); + memcpy(target_data, data, MIN(len, tgt_len)); + if (tgt_len > len) { + memset(target_data + len, 0, tgt_len - len); + } } + target_cmsg->cmsg_len = tswapal(tgt_len); + tgt_space = TARGET_CMSG_SPACE(tgt_len); + if (msg_controllen < tgt_space) { + tgt_space = msg_controllen; + } + msg_controllen -= tgt_space; + space += tgt_space; cmsg = CMSG_NXTHDR(msgh, cmsg); target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); } @@ -3277,6 +3332,7 @@ static abi_long do_ipc(unsigned int call, abi_long first, #define STRUCT_SPECIAL(name) STRUCT_ ## name, enum { #include "syscall_types.h" +STRUCT_MAX }; #undef STRUCT #undef STRUCT_SPECIAL @@ -3290,7 +3346,7 @@ enum { typedef struct IOCTLEntry IOCTLEntry; typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp, - int fd, abi_long cmd, abi_long arg); + int fd, int cmd, abi_long arg); struct IOCTLEntry { int target_cmd; @@ -3316,7 +3372,7 @@ struct IOCTLEntry { / sizeof(struct fiemap_extent)) static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp, - int fd, abi_long cmd, abi_long arg) + int fd, int cmd, abi_long arg) { /* The parameter for this ioctl is a struct fiemap followed * by an array of struct fiemap_extent whose size is set @@ -3397,7 +3453,7 @@ static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp, #endif static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp, - int fd, abi_long cmd, abi_long arg) + int fd, int cmd, abi_long arg) { const argtype *arg_type = ie->arg_type; int target_size; @@ -3491,7 +3547,7 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp, } static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, - abi_long cmd, abi_long arg) + int cmd, abi_long arg) { void *argptr; struct dm_ioctl *host_dm; @@ -3716,7 +3772,7 @@ out: } static abi_long do_ioctl_blkpg(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, - abi_long cmd, abi_long arg) + int cmd, abi_long arg) { void *argptr; int target_size; @@ -3769,7 +3825,7 @@ out: } static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp, - int fd, abi_long cmd, abi_long arg) + int fd, int cmd, abi_long arg) { const argtype *arg_type = ie->arg_type; const StructEntry *se; @@ -3832,7 +3888,7 @@ static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp, } static abi_long do_ioctl_kdsigaccept(const IOCTLEntry *ie, uint8_t *buf_temp, - int fd, abi_long cmd, abi_long arg) + int fd, int cmd, abi_long arg) { int sig = target_to_host_signal(arg); return get_errno(ioctl(fd, ie->host_cmd, sig)); @@ -3849,7 +3905,7 @@ static IOCTLEntry ioctl_entries[] = { /* ??? Implement proper locking for ioctls. */ /* do_ioctl() Must return target values and target errnos. */ -static abi_long do_ioctl(int fd, abi_long cmd, abi_long arg) +static abi_long do_ioctl(int fd, int cmd, abi_long arg) { const IOCTLEntry *ie; const argtype *arg_type; @@ -4879,6 +4935,8 @@ void syscall_init(void) int size; int i; + thunk_init(STRUCT_MAX); + #define STRUCT(name, ...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def); #define STRUCT_SPECIAL(name) thunk_register_struct_direct(STRUCT_ ## name, #name, &struct_ ## name ## _def); #include "syscall_types.h" diff --git a/main-loop.c b/main-loop.c index 981bcb5..82875a4 100644 --- a/main-loop.c +++ b/main-loop.c @@ -100,8 +100,7 @@ static int qemu_signal_init(void) fcntl_setfl(sigfd, O_NONBLOCK); - qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL, - (void *)(intptr_t)sigfd); + qemu_set_fd_handler(sigfd, sigfd_handler, NULL, (void *)(intptr_t)sigfd); return 0; } diff --git a/migration/exec.c b/migration/exec.c index 4790247..8406d2b 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -49,7 +49,7 @@ static void exec_accept_incoming_migration(void *opaque) { QEMUFile *f = opaque; - qemu_set_fd_handler2(qemu_get_fd(f), NULL, NULL, NULL, NULL); + qemu_set_fd_handler(qemu_get_fd(f), NULL, NULL, NULL); process_incoming_migration(f); } @@ -64,6 +64,6 @@ void exec_start_incoming_migration(const char *command, Error **errp) return; } - qemu_set_fd_handler2(qemu_get_fd(f), NULL, - exec_accept_incoming_migration, NULL, f); + qemu_set_fd_handler(qemu_get_fd(f), exec_accept_incoming_migration, NULL, + f); } diff --git a/migration/fd.c b/migration/fd.c index 129da99..3e4bed0 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -62,7 +62,7 @@ static void fd_accept_incoming_migration(void *opaque) { QEMUFile *f = opaque; - qemu_set_fd_handler2(qemu_get_fd(f), NULL, NULL, NULL, NULL); + qemu_set_fd_handler(qemu_get_fd(f), NULL, NULL, NULL); process_incoming_migration(f); } @@ -84,5 +84,5 @@ void fd_start_incoming_migration(const char *infd, Error **errp) return; } - qemu_set_fd_handler2(fd, NULL, fd_accept_incoming_migration, NULL, f); + qemu_set_fd_handler(fd, fd_accept_incoming_migration, NULL, f); } diff --git a/migration/migration.c b/migration/migration.c index 732d229..b04b457 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -53,6 +53,7 @@ static bool deferred_incoming; migrations at once. For now we don't need to add dynamic creation of migration */ +/* For outgoing */ MigrationState *migrate_get_current(void) { static MigrationState current_migration = { @@ -71,6 +72,30 @@ MigrationState *migrate_get_current(void) return ¤t_migration; } +/* For incoming */ +static MigrationIncomingState *mis_current; + +MigrationIncomingState *migration_incoming_get_current(void) +{ + return mis_current; +} + +MigrationIncomingState *migration_incoming_state_new(QEMUFile* f) +{ + mis_current = g_malloc0(sizeof(MigrationIncomingState)); + mis_current->file = f; + QLIST_INIT(&mis_current->loadvm_handlers); + + return mis_current; +} + +void migration_incoming_state_destroy(void) +{ + loadvm_free_handlers(mis_current); + g_free(mis_current); + mis_current = NULL; +} + /* * Called on -incoming with a defer: uri. * The migration can be started later after any parameters have been @@ -115,9 +140,14 @@ static void process_incoming_migration_co(void *opaque) Error *local_err = NULL; int ret; + migration_incoming_state_new(f); + ret = qemu_loadvm_state(f); + qemu_fclose(f); free_xbzrle_decoded_buf(); + migration_incoming_state_destroy(); + if (ret < 0) { error_report("load of migration failed: %s", strerror(-ret)); migrate_decompress_threads_join(); @@ -738,6 +768,7 @@ static void *migration_thread(void *opaque) int64_t start_time = initial_time; bool old_vm_running = false; + qemu_savevm_state_header(s->file); qemu_savevm_state_begin(s->file, &s->params); s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; @@ -838,9 +869,6 @@ static void *migration_thread(void *opaque) void migrate_fd_connect(MigrationState *s) { - s->state = MIGRATION_STATUS_SETUP; - trace_migrate_set_state(MIGRATION_STATUS_SETUP); - /* This is a best 1st approximation. ns to ms */ s->expected_downtime = max_downtime/1000000; s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s); diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 2750365..965a757 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -349,14 +349,14 @@ void qemu_file_skip(QEMUFile *f, int size) } /* - * Read 'size' bytes from file (at 'offset') into buf without moving the - * pointer. + * Read 'size' bytes from file (at 'offset') without moving the + * pointer and set 'buf' to point to that data. * * It will return size bytes unless there was an error, in which case it will * return as many as it managed to read (assuming blocking fd's which * all current QEMUFile are) */ -int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) +int qemu_peek_buffer(QEMUFile *f, uint8_t **buf, int size, size_t offset) { int pending; int index; @@ -392,7 +392,7 @@ int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset) size = pending; } - memcpy(buf, f->buf + index, size); + *buf = f->buf + index; return size; } @@ -411,11 +411,13 @@ int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size) while (pending > 0) { int res; + uint8_t *src; - res = qemu_peek_buffer(f, buf, MIN(pending, IO_BUF_SIZE), 0); + res = qemu_peek_buffer(f, &src, MIN(pending, IO_BUF_SIZE), 0); if (res == 0) { return done; } + memcpy(buf, src, res); qemu_file_skip(f, res); buf += res; pending -= res; @@ -585,3 +587,20 @@ int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src) } return len; } + +/* + * Get a string whose length is determined by a single preceding byte + * A preallocated 256 byte buffer must be passed in. + * Returns: len on success and a 0 terminated string in the buffer + * else 0 + * (Note a 0 length string will return 0 either way) + */ +size_t qemu_get_counted_string(QEMUFile *f, char buf[256]) +{ + size_t len = qemu_get_byte(f); + size_t res = qemu_get_buffer(f, (uint8_t *)buf, len); + + buf[res] = 0; + + return res == len ? res : 0; +} diff --git a/migration/ram.c b/migration/ram.c new file mode 100644 index 0000000..57368e1 --- /dev/null +++ b/migration/ram.c @@ -0,0 +1,1628 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2011-2015 Red Hat Inc + * + * Authors: + * Juan Quintela <quintela@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdint.h> +#include <zlib.h> +#include "qemu/bitops.h" +#include "qemu/bitmap.h" +#include "qemu/timer.h" +#include "qemu/main-loop.h" +#include "migration/migration.h" +#include "exec/address-spaces.h" +#include "migration/page_cache.h" +#include "qemu/error-report.h" +#include "trace.h" +#include "exec/ram_addr.h" +#include "qemu/rcu_queue.h" + +#ifdef DEBUG_MIGRATION_RAM +#define DPRINTF(fmt, ...) \ + do { fprintf(stdout, "migration_ram: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +static bool mig_throttle_on; +static int dirty_rate_high_cnt; +static void check_guest_throttling(void); + +static uint64_t bitmap_sync_count; + +/***********************************************************/ +/* ram save/restore */ + +#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */ +#define RAM_SAVE_FLAG_COMPRESS 0x02 +#define RAM_SAVE_FLAG_MEM_SIZE 0x04 +#define RAM_SAVE_FLAG_PAGE 0x08 +#define RAM_SAVE_FLAG_EOS 0x10 +#define RAM_SAVE_FLAG_CONTINUE 0x20 +#define RAM_SAVE_FLAG_XBZRLE 0x40 +/* 0x80 is reserved in migration.h start with 0x100 next */ +#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100 + +static const uint8_t ZERO_TARGET_PAGE[TARGET_PAGE_SIZE]; + +static inline bool is_zero_range(uint8_t *p, uint64_t size) +{ + return buffer_find_nonzero_offset(p, size) == size; +} + +/* 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; + /* Cache for XBZRLE, Protected by lock. */ + PageCache *cache; + QemuMutex lock; +} XBZRLE; + +/* buffer used for XBZRLE decoding */ +static uint8_t *xbzrle_decoded_buf; + +static void XBZRLE_cache_lock(void) +{ + if (migrate_use_xbzrle()) + qemu_mutex_lock(&XBZRLE.lock); +} + +static void XBZRLE_cache_unlock(void) +{ + if (migrate_use_xbzrle()) + qemu_mutex_unlock(&XBZRLE.lock); +} + +/* + * called from qmp_migrate_set_cache_size in main thread, possibly while + * a migration is in progress. + * A running migration maybe using the cache and might finish during this + * call, hence changes to the cache are protected by XBZRLE.lock(). + */ +int64_t xbzrle_cache_resize(int64_t new_size) +{ + PageCache *new_cache; + int64_t ret; + + if (new_size < TARGET_PAGE_SIZE) { + return -1; + } + + XBZRLE_cache_lock(); + + if (XBZRLE.cache != NULL) { + if (pow2floor(new_size) == migrate_xbzrle_cache_size()) { + goto out_new_size; + } + new_cache = cache_init(new_size / TARGET_PAGE_SIZE, + TARGET_PAGE_SIZE); + if (!new_cache) { + error_report("Error creating cache"); + ret = -1; + goto out; + } + + cache_fini(XBZRLE.cache); + XBZRLE.cache = new_cache; + } + +out_new_size: + ret = pow2floor(new_size); +out: + XBZRLE_cache_unlock(); + return ret; +} + +/* accounting for migration statistics */ +typedef struct AccountingInfo { + uint64_t dup_pages; + uint64_t skipped_pages; + uint64_t norm_pages; + uint64_t iterations; + uint64_t xbzrle_bytes; + uint64_t xbzrle_pages; + uint64_t xbzrle_cache_miss; + double xbzrle_cache_miss_rate; + 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 skipped_mig_bytes_transferred(void) +{ + return acct_info.skipped_pages * TARGET_PAGE_SIZE; +} + +uint64_t skipped_mig_pages_transferred(void) +{ + return acct_info.skipped_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; +} + +double xbzrle_mig_cache_miss_rate(void) +{ + return acct_info.xbzrle_cache_miss_rate; +} + +uint64_t xbzrle_mig_pages_overflow(void) +{ + return acct_info.xbzrle_overflows; +} + +/* This is the last block that we have visited serching for dirty pages + */ +static RAMBlock *last_seen_block; +/* This is the last block from where we have sent data */ +static RAMBlock *last_sent_block; +static ram_addr_t last_offset; +static unsigned long *migration_bitmap; +static uint64_t migration_dirty_pages; +static uint32_t last_version; +static bool ram_bulk_stage; + +struct CompressParam { + bool start; + bool done; + QEMUFile *file; + QemuMutex mutex; + QemuCond cond; + RAMBlock *block; + ram_addr_t offset; +}; +typedef struct CompressParam CompressParam; + +struct DecompressParam { + bool start; + QemuMutex mutex; + QemuCond cond; + void *des; + uint8 *compbuf; + int len; +}; +typedef struct DecompressParam DecompressParam; + +static CompressParam *comp_param; +static QemuThread *compress_threads; +/* comp_done_cond is used to wake up the migration thread when + * one of the compression threads has finished the compression. + * comp_done_lock is used to co-work with comp_done_cond. + */ +static QemuMutex *comp_done_lock; +static QemuCond *comp_done_cond; +/* The empty QEMUFileOps will be used by file in CompressParam */ +static const QEMUFileOps empty_ops = { }; + +static bool compression_switch; +static bool quit_comp_thread; +static bool quit_decomp_thread; +static DecompressParam *decomp_param; +static QemuThread *decompress_threads; +static uint8_t *compressed_data_buf; + +static int do_compress_ram_page(CompressParam *param); + +static void *do_data_compress(void *opaque) +{ + CompressParam *param = opaque; + + while (!quit_comp_thread) { + qemu_mutex_lock(¶m->mutex); + /* Re-check the quit_comp_thread in case of + * terminate_compression_threads is called just before + * qemu_mutex_lock(¶m->mutex) and after + * while(!quit_comp_thread), re-check it here can make + * sure the compression thread terminate as expected. + */ + while (!param->start && !quit_comp_thread) { + qemu_cond_wait(¶m->cond, ¶m->mutex); + } + if (!quit_comp_thread) { + do_compress_ram_page(param); + } + param->start = false; + qemu_mutex_unlock(¶m->mutex); + + qemu_mutex_lock(comp_done_lock); + param->done = true; + qemu_cond_signal(comp_done_cond); + qemu_mutex_unlock(comp_done_lock); + } + + return NULL; +} + +static inline void terminate_compression_threads(void) +{ + int idx, thread_count; + + thread_count = migrate_compress_threads(); + quit_comp_thread = true; + for (idx = 0; idx < thread_count; idx++) { + qemu_mutex_lock(&comp_param[idx].mutex); + qemu_cond_signal(&comp_param[idx].cond); + qemu_mutex_unlock(&comp_param[idx].mutex); + } +} + +void migrate_compress_threads_join(void) +{ + int i, thread_count; + + if (!migrate_use_compression()) { + return; + } + terminate_compression_threads(); + thread_count = migrate_compress_threads(); + for (i = 0; i < thread_count; i++) { + qemu_thread_join(compress_threads + i); + qemu_fclose(comp_param[i].file); + qemu_mutex_destroy(&comp_param[i].mutex); + qemu_cond_destroy(&comp_param[i].cond); + } + qemu_mutex_destroy(comp_done_lock); + qemu_cond_destroy(comp_done_cond); + g_free(compress_threads); + g_free(comp_param); + g_free(comp_done_cond); + g_free(comp_done_lock); + compress_threads = NULL; + comp_param = NULL; + comp_done_cond = NULL; + comp_done_lock = NULL; +} + +void migrate_compress_threads_create(void) +{ + int i, thread_count; + + if (!migrate_use_compression()) { + return; + } + quit_comp_thread = false; + compression_switch = true; + thread_count = migrate_compress_threads(); + compress_threads = g_new0(QemuThread, thread_count); + comp_param = g_new0(CompressParam, thread_count); + comp_done_cond = g_new0(QemuCond, 1); + comp_done_lock = g_new0(QemuMutex, 1); + qemu_cond_init(comp_done_cond); + qemu_mutex_init(comp_done_lock); + for (i = 0; i < thread_count; i++) { + /* com_param[i].file is just used as a dummy buffer to save data, set + * it's ops to empty. + */ + comp_param[i].file = qemu_fopen_ops(NULL, &empty_ops); + comp_param[i].done = true; + qemu_mutex_init(&comp_param[i].mutex); + qemu_cond_init(&comp_param[i].cond); + qemu_thread_create(compress_threads + i, "compress", + do_data_compress, comp_param + i, + QEMU_THREAD_JOINABLE); + } +} + +/** + * save_page_header: Write page header to wire + * + * If this is the 1st block, it also writes the block identification + * + * Returns: Number of bytes written + * + * @f: QEMUFile where to send the data + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * in the lower bits, it contains flags + */ +static size_t save_page_header(QEMUFile *f, RAMBlock *block, ram_addr_t offset) +{ + size_t size; + + qemu_put_be64(f, offset); + size = 8; + + if (!(offset & RAM_SAVE_FLAG_CONTINUE)) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, + strlen(block->idstr)); + size += 1 + strlen(block->idstr); + } + return size; +} + +/* Update the xbzrle cache to reflect a page that's been sent as all 0. + * The important thing is that a stale (not-yet-0'd) page be replaced + * by the new data. + * As a bonus, if the page wasn't in the cache it gets added so that + * when a small write is made into the 0'd page it gets XBZRLE sent + */ +static void xbzrle_cache_zero_page(ram_addr_t current_addr) +{ + if (ram_bulk_stage || !migrate_use_xbzrle()) { + return; + } + + /* We don't care if this fails to allocate a new cache page + * as long as it updated an old one */ + cache_insert(XBZRLE.cache, current_addr, ZERO_TARGET_PAGE, + bitmap_sync_count); +} + +#define ENCODING_FLAG_XBZRLE 0x1 + +/** + * save_xbzrle_page: compress and send current page + * + * Returns: 1 means that we wrote the page + * 0 means that page is identical to the one already sent + * -1 means that xbzrle would be longer than normal + * + * @f: QEMUFile where to send the data + * @current_data: + * @current_addr: + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + */ +static int save_xbzrle_page(QEMUFile *f, uint8_t **current_data, + ram_addr_t current_addr, RAMBlock *block, + ram_addr_t offset, bool last_stage, + uint64_t *bytes_transferred) +{ + int encoded_len = 0, bytes_xbzrle; + uint8_t *prev_cached_page; + + if (!cache_is_cached(XBZRLE.cache, current_addr, bitmap_sync_count)) { + acct_info.xbzrle_cache_miss++; + if (!last_stage) { + if (cache_insert(XBZRLE.cache, current_addr, *current_data, + bitmap_sync_count) == -1) { + return -1; + } else { + /* update *current_data when the page has been + inserted into cache */ + *current_data = get_cached_data(XBZRLE.cache, current_addr); + } + } + 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 */ + if (!last_stage) { + memcpy(prev_cached_page, *current_data, TARGET_PAGE_SIZE); + *current_data = prev_cached_page; + } + 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 */ + bytes_xbzrle = save_page_header(f, block, offset | 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_xbzrle += encoded_len + 1 + 2; + acct_info.xbzrle_pages++; + acct_info.xbzrle_bytes += bytes_xbzrle; + *bytes_transferred += bytes_xbzrle; + + return 1; +} + +static inline +ram_addr_t migration_bitmap_find_and_reset_dirty(MemoryRegion *mr, + ram_addr_t start) +{ + unsigned long base = mr->ram_addr >> TARGET_PAGE_BITS; + unsigned long nr = base + (start >> TARGET_PAGE_BITS); + uint64_t mr_size = TARGET_PAGE_ALIGN(memory_region_size(mr)); + unsigned long size = base + (mr_size >> TARGET_PAGE_BITS); + + unsigned long next; + + if (ram_bulk_stage && nr > base) { + next = nr + 1; + } else { + next = find_next_bit(migration_bitmap, size, nr); + } + + if (next < size) { + clear_bit(next, migration_bitmap); + migration_dirty_pages--; + } + return (next - base) << TARGET_PAGE_BITS; +} + +static void migration_bitmap_sync_range(ram_addr_t start, ram_addr_t length) +{ + migration_dirty_pages += + cpu_physical_memory_sync_dirty_bitmap(migration_bitmap, start, length); +} + + +/* Fix me: there are too many global variables used in migration process. */ +static int64_t start_time; +static int64_t bytes_xfer_prev; +static int64_t num_dirty_pages_period; +static uint64_t xbzrle_cache_miss_prev; +static uint64_t iterations_prev; + +static void migration_bitmap_sync_init(void) +{ + start_time = 0; + bytes_xfer_prev = 0; + num_dirty_pages_period = 0; + xbzrle_cache_miss_prev = 0; + iterations_prev = 0; +} + +/* Called with iothread lock held, to protect ram_list.dirty_memory[] */ +static void migration_bitmap_sync(void) +{ + RAMBlock *block; + uint64_t num_dirty_pages_init = migration_dirty_pages; + MigrationState *s = migrate_get_current(); + int64_t end_time; + int64_t bytes_xfer_now; + + bitmap_sync_count++; + + if (!bytes_xfer_prev) { + bytes_xfer_prev = ram_bytes_transferred(); + } + + if (!start_time) { + start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + } + + trace_migration_bitmap_sync_start(); + address_space_sync_dirty_bitmap(&address_space_memory); + + rcu_read_lock(); + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + migration_bitmap_sync_range(block->mr->ram_addr, block->used_length); + } + rcu_read_unlock(); + + trace_migration_bitmap_sync_end(migration_dirty_pages + - num_dirty_pages_init); + num_dirty_pages_period += migration_dirty_pages - num_dirty_pages_init; + end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + + /* more than 1 second = 1000 millisecons */ + if (end_time > start_time + 1000) { + if (migrate_auto_converge()) { + /* The following detection logic can be refined later. For now: + Check to see if the dirtied bytes is 50% more than the approx. + amount of bytes that just got transferred since the last time we + were in this routine. If that happens >N times (for now N==4) + we turn on the throttle down logic */ + bytes_xfer_now = ram_bytes_transferred(); + if (s->dirty_pages_rate && + (num_dirty_pages_period * TARGET_PAGE_SIZE > + (bytes_xfer_now - bytes_xfer_prev)/2) && + (dirty_rate_high_cnt++ > 4)) { + trace_migration_throttle(); + mig_throttle_on = true; + dirty_rate_high_cnt = 0; + } + bytes_xfer_prev = bytes_xfer_now; + } else { + mig_throttle_on = false; + } + if (migrate_use_xbzrle()) { + if (iterations_prev != acct_info.iterations) { + acct_info.xbzrle_cache_miss_rate = + (double)(acct_info.xbzrle_cache_miss - + xbzrle_cache_miss_prev) / + (acct_info.iterations - iterations_prev); + } + iterations_prev = acct_info.iterations; + xbzrle_cache_miss_prev = acct_info.xbzrle_cache_miss; + } + s->dirty_pages_rate = num_dirty_pages_period * 1000 + / (end_time - start_time); + s->dirty_bytes_rate = s->dirty_pages_rate * TARGET_PAGE_SIZE; + start_time = end_time; + num_dirty_pages_period = 0; + } + s->dirty_sync_count = bitmap_sync_count; +} + +/** + * save_zero_page: Send the zero page to the stream + * + * Returns: Number of pages written. + * + * @f: QEMUFile where to send the data + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @p: pointer to the page + * @bytes_transferred: increase it with the number of transferred bytes + */ +static int save_zero_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset, + uint8_t *p, uint64_t *bytes_transferred) +{ + int pages = -1; + + if (is_zero_range(p, TARGET_PAGE_SIZE)) { + acct_info.dup_pages++; + *bytes_transferred += save_page_header(f, block, + offset | RAM_SAVE_FLAG_COMPRESS); + qemu_put_byte(f, 0); + *bytes_transferred += 1; + pages = 1; + } + + return pages; +} + +/** + * ram_save_page: Send the given page to the stream + * + * Returns: Number of pages written. + * + * @f: QEMUFile where to send the data + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + */ +static int ram_save_page(QEMUFile *f, RAMBlock* block, ram_addr_t offset, + bool last_stage, uint64_t *bytes_transferred) +{ + int pages = -1; + uint64_t bytes_xmit; + ram_addr_t current_addr; + MemoryRegion *mr = block->mr; + uint8_t *p; + int ret; + bool send_async = true; + + p = memory_region_get_ram_ptr(mr) + offset; + + /* In doubt sent page as normal */ + bytes_xmit = 0; + ret = ram_control_save_page(f, block->offset, + offset, TARGET_PAGE_SIZE, &bytes_xmit); + if (bytes_xmit) { + *bytes_transferred += bytes_xmit; + pages = 1; + } + + XBZRLE_cache_lock(); + + current_addr = block->offset + offset; + + if (block == last_sent_block) { + offset |= RAM_SAVE_FLAG_CONTINUE; + } + if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { + if (ret != RAM_SAVE_CONTROL_DELAYED) { + if (bytes_xmit > 0) { + acct_info.norm_pages++; + } else if (bytes_xmit == 0) { + acct_info.dup_pages++; + } + } + } else { + pages = save_zero_page(f, block, offset, p, bytes_transferred); + if (pages > 0) { + /* Must let xbzrle know, otherwise a previous (now 0'd) cached + * page would be stale + */ + xbzrle_cache_zero_page(current_addr); + } else if (!ram_bulk_stage && migrate_use_xbzrle()) { + pages = save_xbzrle_page(f, &p, current_addr, block, + offset, last_stage, bytes_transferred); + if (!last_stage) { + /* Can't send this cached data async, since the cache page + * might get updated before it gets to the wire + */ + send_async = false; + } + } + } + + /* XBZRLE overflow or normal page */ + if (pages == -1) { + *bytes_transferred += save_page_header(f, block, + offset | RAM_SAVE_FLAG_PAGE); + if (send_async) { + qemu_put_buffer_async(f, p, TARGET_PAGE_SIZE); + } else { + qemu_put_buffer(f, p, TARGET_PAGE_SIZE); + } + *bytes_transferred += TARGET_PAGE_SIZE; + pages = 1; + acct_info.norm_pages++; + } + + XBZRLE_cache_unlock(); + + return pages; +} + +static int do_compress_ram_page(CompressParam *param) +{ + int bytes_sent, blen; + uint8_t *p; + RAMBlock *block = param->block; + ram_addr_t offset = param->offset; + + p = memory_region_get_ram_ptr(block->mr) + (offset & TARGET_PAGE_MASK); + + bytes_sent = save_page_header(param->file, block, offset | + RAM_SAVE_FLAG_COMPRESS_PAGE); + blen = qemu_put_compression_data(param->file, p, TARGET_PAGE_SIZE, + migrate_compress_level()); + bytes_sent += blen; + + return bytes_sent; +} + +static inline void start_compression(CompressParam *param) +{ + param->done = false; + qemu_mutex_lock(¶m->mutex); + param->start = true; + qemu_cond_signal(¶m->cond); + qemu_mutex_unlock(¶m->mutex); +} + +static inline void start_decompression(DecompressParam *param) +{ + qemu_mutex_lock(¶m->mutex); + param->start = true; + qemu_cond_signal(¶m->cond); + qemu_mutex_unlock(¶m->mutex); +} + +static uint64_t bytes_transferred; + +static void flush_compressed_data(QEMUFile *f) +{ + int idx, len, thread_count; + + if (!migrate_use_compression()) { + return; + } + thread_count = migrate_compress_threads(); + for (idx = 0; idx < thread_count; idx++) { + if (!comp_param[idx].done) { + qemu_mutex_lock(comp_done_lock); + while (!comp_param[idx].done && !quit_comp_thread) { + qemu_cond_wait(comp_done_cond, comp_done_lock); + } + qemu_mutex_unlock(comp_done_lock); + } + if (!quit_comp_thread) { + len = qemu_put_qemu_file(f, comp_param[idx].file); + bytes_transferred += len; + } + } +} + +static inline void set_compress_params(CompressParam *param, RAMBlock *block, + ram_addr_t offset) +{ + param->block = block; + param->offset = offset; +} + +static int compress_page_with_multi_thread(QEMUFile *f, RAMBlock *block, + ram_addr_t offset, + uint64_t *bytes_transferred) +{ + int idx, thread_count, bytes_xmit = -1, pages = -1; + + thread_count = migrate_compress_threads(); + qemu_mutex_lock(comp_done_lock); + while (true) { + for (idx = 0; idx < thread_count; idx++) { + if (comp_param[idx].done) { + bytes_xmit = qemu_put_qemu_file(f, comp_param[idx].file); + set_compress_params(&comp_param[idx], block, offset); + start_compression(&comp_param[idx]); + pages = 1; + acct_info.norm_pages++; + *bytes_transferred += bytes_xmit; + break; + } + } + if (pages > 0) { + break; + } else { + qemu_cond_wait(comp_done_cond, comp_done_lock); + } + } + qemu_mutex_unlock(comp_done_lock); + + return pages; +} + +/** + * ram_save_compressed_page: compress the given page and send it to the stream + * + * Returns: Number of pages written. + * + * @f: QEMUFile where to send the data + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + */ +static int ram_save_compressed_page(QEMUFile *f, RAMBlock *block, + ram_addr_t offset, bool last_stage, + uint64_t *bytes_transferred) +{ + int pages = -1; + uint64_t bytes_xmit; + MemoryRegion *mr = block->mr; + uint8_t *p; + int ret; + + p = memory_region_get_ram_ptr(mr) + offset; + + bytes_xmit = 0; + ret = ram_control_save_page(f, block->offset, + offset, TARGET_PAGE_SIZE, &bytes_xmit); + if (bytes_xmit) { + *bytes_transferred += bytes_xmit; + pages = 1; + } + if (block == last_sent_block) { + offset |= RAM_SAVE_FLAG_CONTINUE; + } + if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { + if (ret != RAM_SAVE_CONTROL_DELAYED) { + if (bytes_xmit > 0) { + acct_info.norm_pages++; + } else if (bytes_xmit == 0) { + acct_info.dup_pages++; + } + } + } else { + /* When starting the process of a new block, the first page of + * the block should be sent out before other pages in the same + * block, and all the pages in last block should have been sent + * out, keeping this order is important, because the 'cont' flag + * is used to avoid resending the block name. + */ + if (block != last_sent_block) { + flush_compressed_data(f); + pages = save_zero_page(f, block, offset, p, bytes_transferred); + if (pages == -1) { + set_compress_params(&comp_param[0], block, offset); + /* Use the qemu thread to compress the data to make sure the + * first page is sent out before other pages + */ + bytes_xmit = do_compress_ram_page(&comp_param[0]); + acct_info.norm_pages++; + qemu_put_qemu_file(f, comp_param[0].file); + *bytes_transferred += bytes_xmit; + pages = 1; + } + } else { + pages = save_zero_page(f, block, offset, p, bytes_transferred); + if (pages == -1) { + pages = compress_page_with_multi_thread(f, block, offset, + bytes_transferred); + } + } + } + + return pages; +} + +/** + * ram_find_and_save_block: Finds a dirty page and sends it to f + * + * Called within an RCU critical section. + * + * Returns: The number of pages written + * 0 means no dirty pages + * + * @f: QEMUFile where to send the data + * @last_stage: if we are at the completion stage + * @bytes_transferred: increase it with the number of transferred bytes + */ + +static int ram_find_and_save_block(QEMUFile *f, bool last_stage, + uint64_t *bytes_transferred) +{ + RAMBlock *block = last_seen_block; + ram_addr_t offset = last_offset; + bool complete_round = false; + int pages = 0; + MemoryRegion *mr; + + if (!block) + block = QLIST_FIRST_RCU(&ram_list.blocks); + + while (true) { + mr = block->mr; + offset = migration_bitmap_find_and_reset_dirty(mr, offset); + if (complete_round && block == last_seen_block && + offset >= last_offset) { + break; + } + if (offset >= block->used_length) { + offset = 0; + block = QLIST_NEXT_RCU(block, next); + if (!block) { + block = QLIST_FIRST_RCU(&ram_list.blocks); + complete_round = true; + ram_bulk_stage = false; + if (migrate_use_xbzrle()) { + /* If xbzrle is on, stop using the data compression at this + * point. In theory, xbzrle can do better than compression. + */ + flush_compressed_data(f); + compression_switch = false; + } + } + } else { + if (compression_switch && migrate_use_compression()) { + pages = ram_save_compressed_page(f, block, offset, last_stage, + bytes_transferred); + } else { + pages = ram_save_page(f, block, offset, last_stage, + bytes_transferred); + } + + /* if page is unmodified, continue to the next */ + if (pages > 0) { + last_sent_block = block; + break; + } + } + } + + last_seen_block = block; + last_offset = offset; + + return pages; +} + +void acct_update_position(QEMUFile *f, size_t size, bool zero) +{ + uint64_t pages = size / TARGET_PAGE_SIZE; + if (zero) { + acct_info.dup_pages += pages; + } else { + acct_info.norm_pages += pages; + bytes_transferred += size; + qemu_update_position(f, size); + } +} + +static ram_addr_t ram_save_remaining(void) +{ + return migration_dirty_pages; +} + +uint64_t ram_bytes_remaining(void) +{ + return ram_save_remaining() * TARGET_PAGE_SIZE; +} + +uint64_t ram_bytes_transferred(void) +{ + return bytes_transferred; +} + +uint64_t ram_bytes_total(void) +{ + RAMBlock *block; + uint64_t total = 0; + + rcu_read_lock(); + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) + total += block->used_length; + rcu_read_unlock(); + return total; +} + +void free_xbzrle_decoded_buf(void) +{ + g_free(xbzrle_decoded_buf); + xbzrle_decoded_buf = NULL; +} + +static void migration_end(void) +{ + if (migration_bitmap) { + memory_global_dirty_log_stop(); + g_free(migration_bitmap); + migration_bitmap = NULL; + } + + XBZRLE_cache_lock(); + if (XBZRLE.cache) { + cache_fini(XBZRLE.cache); + g_free(XBZRLE.encoded_buf); + g_free(XBZRLE.current_buf); + XBZRLE.cache = NULL; + XBZRLE.encoded_buf = NULL; + XBZRLE.current_buf = NULL; + } + XBZRLE_cache_unlock(); +} + +static void ram_migration_cancel(void *opaque) +{ + migration_end(); +} + +static void reset_ram_globals(void) +{ + last_seen_block = NULL; + last_sent_block = NULL; + last_offset = 0; + last_version = ram_list.version; + ram_bulk_stage = true; +} + +#define MAX_WAIT 50 /* ms, half buffered_file limit */ + + +/* Each of ram_save_setup, ram_save_iterate and ram_save_complete has + * long-running RCU critical section. When rcu-reclaims in the code + * start to become numerous it will be necessary to reduce the + * granularity of these critical sections. + */ + +static int ram_save_setup(QEMUFile *f, void *opaque) +{ + RAMBlock *block; + int64_t ram_bitmap_pages; /* Size of bitmap in pages, including gaps */ + + mig_throttle_on = false; + dirty_rate_high_cnt = 0; + bitmap_sync_count = 0; + migration_bitmap_sync_init(); + + if (migrate_use_xbzrle()) { + XBZRLE_cache_lock(); + XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() / + TARGET_PAGE_SIZE, + TARGET_PAGE_SIZE); + if (!XBZRLE.cache) { + XBZRLE_cache_unlock(); + error_report("Error creating cache"); + return -1; + } + XBZRLE_cache_unlock(); + + /* We prefer not to abort if there is no memory */ + XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE); + if (!XBZRLE.encoded_buf) { + error_report("Error allocating encoded_buf"); + return -1; + } + + XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE); + if (!XBZRLE.current_buf) { + error_report("Error allocating current_buf"); + g_free(XBZRLE.encoded_buf); + XBZRLE.encoded_buf = NULL; + return -1; + } + + acct_clear(); + } + + /* iothread lock needed for ram_list.dirty_memory[] */ + qemu_mutex_lock_iothread(); + qemu_mutex_lock_ramlist(); + rcu_read_lock(); + bytes_transferred = 0; + reset_ram_globals(); + + ram_bitmap_pages = last_ram_offset() >> TARGET_PAGE_BITS; + migration_bitmap = bitmap_new(ram_bitmap_pages); + bitmap_set(migration_bitmap, 0, ram_bitmap_pages); + + /* + * Count the total number of pages used by ram blocks not including any + * gaps due to alignment or unplugs. + */ + migration_dirty_pages = ram_bytes_total() >> TARGET_PAGE_BITS; + + memory_global_dirty_log_start(); + migration_bitmap_sync(); + qemu_mutex_unlock_ramlist(); + qemu_mutex_unlock_iothread(); + + qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); + + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); + qemu_put_be64(f, block->used_length); + } + + rcu_read_unlock(); + + ram_control_before_iterate(f, RAM_CONTROL_SETUP); + ram_control_after_iterate(f, RAM_CONTROL_SETUP); + + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + + return 0; +} + +static int ram_save_iterate(QEMUFile *f, void *opaque) +{ + int ret; + int i; + int64_t t0; + int pages_sent = 0; + + rcu_read_lock(); + if (ram_list.version != last_version) { + reset_ram_globals(); + } + + /* Read version before ram_list.blocks */ + smp_rmb(); + + ram_control_before_iterate(f, RAM_CONTROL_ROUND); + + t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + i = 0; + while ((ret = qemu_file_rate_limit(f)) == 0) { + int pages; + + pages = ram_find_and_save_block(f, false, &bytes_transferred); + /* no more pages to sent */ + if (pages == 0) { + break; + } + pages_sent += pages; + acct_info.iterations++; + check_guest_throttling(); + /* 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 + iterations + */ + if ((i & 63) == 0) { + uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) / 1000000; + if (t1 > MAX_WAIT) { + DPRINTF("big wait: %" PRIu64 " milliseconds, %d iterations\n", + t1, i); + break; + } + } + i++; + } + flush_compressed_data(f); + rcu_read_unlock(); + + /* + * Must occur before EOS (or any QEMUFile operation) + * because of RDMA protocol. + */ + ram_control_after_iterate(f, RAM_CONTROL_ROUND); + + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + bytes_transferred += 8; + + ret = qemu_file_get_error(f); + if (ret < 0) { + return ret; + } + + return pages_sent; +} + +/* Called with iothread lock */ +static int ram_save_complete(QEMUFile *f, void *opaque) +{ + rcu_read_lock(); + + migration_bitmap_sync(); + + ram_control_before_iterate(f, RAM_CONTROL_FINISH); + + /* try transferring iterative blocks of memory */ + + /* flush all remaining blocks regardless of rate limiting */ + while (true) { + int pages; + + pages = ram_find_and_save_block(f, true, &bytes_transferred); + /* no more blocks to sent */ + if (pages == 0) { + break; + } + } + + flush_compressed_data(f); + ram_control_after_iterate(f, RAM_CONTROL_FINISH); + migration_end(); + + rcu_read_unlock(); + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + + return 0; +} + +static uint64_t ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size) +{ + uint64_t remaining_size; + + remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; + + if (remaining_size < max_size) { + qemu_mutex_lock_iothread(); + rcu_read_lock(); + migration_bitmap_sync(); + rcu_read_unlock(); + qemu_mutex_unlock_iothread(); + remaining_size = ram_save_remaining() * TARGET_PAGE_SIZE; + } + return remaining_size; +} + +static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host) +{ + 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) { + error_report("Failed to load XBZRLE page - wrong compression!"); + return -1; + } + + if (xh_len > TARGET_PAGE_SIZE) { + error_report("Failed to load XBZRLE page - len overflow!"); + return -1; + } + /* load data and decode */ + qemu_get_buffer(f, xbzrle_decoded_buf, xh_len); + + /* decode RLE */ + if (xbzrle_decode_buffer(xbzrle_decoded_buf, xh_len, host, + TARGET_PAGE_SIZE) == -1) { + error_report("Failed to load XBZRLE page - decode error!"); + return -1; + } + + return 0; +} + +/* Must be called from within a rcu critical section. + * Returns a pointer from within the RCU-protected ram_list. + */ +static inline void *host_from_stream_offset(QEMUFile *f, + ram_addr_t offset, + int flags) +{ + static RAMBlock *block = NULL; + char id[256]; + uint8_t len; + + if (flags & RAM_SAVE_FLAG_CONTINUE) { + if (!block || block->max_length <= offset) { + error_report("Ack, bad migration stream!"); + return NULL; + } + + return memory_region_get_ram_ptr(block->mr) + offset; + } + + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)id, len); + id[len] = 0; + + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + if (!strncmp(id, block->idstr, sizeof(id)) && + block->max_length > offset) { + return memory_region_get_ram_ptr(block->mr) + offset; + } + } + + error_report("Can't find block %s!", id); + return NULL; +} + +/* + * If a page (or a whole RDMA chunk) has been + * determined to be zero, then zap it. + */ +void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) +{ + if (ch != 0 || !is_zero_range(host, size)) { + memset(host, ch, size); + } +} + +static void *do_data_decompress(void *opaque) +{ + DecompressParam *param = opaque; + unsigned long pagesize; + + while (!quit_decomp_thread) { + qemu_mutex_lock(¶m->mutex); + while (!param->start && !quit_decomp_thread) { + qemu_cond_wait(¶m->cond, ¶m->mutex); + pagesize = TARGET_PAGE_SIZE; + if (!quit_decomp_thread) { + /* uncompress() will return failed in some case, especially + * when the page is dirted when doing the compression, it's + * not a problem because the dirty page will be retransferred + * and uncompress() won't break the data in other pages. + */ + uncompress((Bytef *)param->des, &pagesize, + (const Bytef *)param->compbuf, param->len); + } + param->start = false; + } + qemu_mutex_unlock(¶m->mutex); + } + + return NULL; +} + +void migrate_decompress_threads_create(void) +{ + int i, thread_count; + + thread_count = migrate_decompress_threads(); + decompress_threads = g_new0(QemuThread, thread_count); + decomp_param = g_new0(DecompressParam, thread_count); + compressed_data_buf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); + quit_decomp_thread = false; + for (i = 0; i < thread_count; i++) { + qemu_mutex_init(&decomp_param[i].mutex); + qemu_cond_init(&decomp_param[i].cond); + decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); + qemu_thread_create(decompress_threads + i, "decompress", + do_data_decompress, decomp_param + i, + QEMU_THREAD_JOINABLE); + } +} + +void migrate_decompress_threads_join(void) +{ + int i, thread_count; + + quit_decomp_thread = true; + thread_count = migrate_decompress_threads(); + for (i = 0; i < thread_count; i++) { + qemu_mutex_lock(&decomp_param[i].mutex); + qemu_cond_signal(&decomp_param[i].cond); + qemu_mutex_unlock(&decomp_param[i].mutex); + } + for (i = 0; i < thread_count; i++) { + qemu_thread_join(decompress_threads + i); + qemu_mutex_destroy(&decomp_param[i].mutex); + qemu_cond_destroy(&decomp_param[i].cond); + g_free(decomp_param[i].compbuf); + } + g_free(decompress_threads); + g_free(decomp_param); + g_free(compressed_data_buf); + decompress_threads = NULL; + decomp_param = NULL; + compressed_data_buf = NULL; +} + +static void decompress_data_with_multi_threads(uint8_t *compbuf, + void *host, int len) +{ + int idx, thread_count; + + thread_count = migrate_decompress_threads(); + while (true) { + for (idx = 0; idx < thread_count; idx++) { + if (!decomp_param[idx].start) { + memcpy(decomp_param[idx].compbuf, compbuf, len); + decomp_param[idx].des = host; + decomp_param[idx].len = len; + start_decompression(&decomp_param[idx]); + break; + } + } + if (idx < thread_count) { + break; + } + } +} + +static int ram_load(QEMUFile *f, void *opaque, int version_id) +{ + int flags = 0, ret = 0; + static uint64_t seq_iter; + int len = 0; + + seq_iter++; + + if (version_id != 4) { + ret = -EINVAL; + } + + /* This RCU critical section can be very long running. + * When RCU reclaims in the code start to become numerous, + * it will be necessary to reduce the granularity of this + * critical section. + */ + rcu_read_lock(); + while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) { + ram_addr_t addr, total_ram_bytes; + void *host; + uint8_t ch; + + addr = qemu_get_be64(f); + flags = addr & ~TARGET_PAGE_MASK; + addr &= TARGET_PAGE_MASK; + + switch (flags & ~RAM_SAVE_FLAG_CONTINUE) { + case RAM_SAVE_FLAG_MEM_SIZE: + /* Synchronize RAM block list */ + total_ram_bytes = addr; + while (!ret && total_ram_bytes) { + RAMBlock *block; + char id[256]; + ram_addr_t length; + + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)id, len); + id[len] = 0; + length = qemu_get_be64(f); + + QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + if (!strncmp(id, block->idstr, sizeof(id))) { + if (length != block->used_length) { + Error *local_err = NULL; + + ret = qemu_ram_resize(block->offset, length, &local_err); + if (local_err) { + error_report_err(local_err); + } + } + break; + } + } + + if (!block) { + error_report("Unknown ramblock \"%s\", cannot " + "accept migration", id); + ret = -EINVAL; + } + + total_ram_bytes -= length; + } + break; + case RAM_SAVE_FLAG_COMPRESS: + host = host_from_stream_offset(f, addr, flags); + if (!host) { + error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + ch = qemu_get_byte(f); + ram_handle_compressed(host, ch, TARGET_PAGE_SIZE); + break; + case RAM_SAVE_FLAG_PAGE: + host = host_from_stream_offset(f, addr, flags); + if (!host) { + error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + qemu_get_buffer(f, host, TARGET_PAGE_SIZE); + break; + case RAM_SAVE_FLAG_COMPRESS_PAGE: + host = host_from_stream_offset(f, addr, flags); + if (!host) { + error_report("Invalid RAM offset " RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + + len = qemu_get_be32(f); + if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { + error_report("Invalid compressed data length: %d", len); + ret = -EINVAL; + break; + } + qemu_get_buffer(f, compressed_data_buf, len); + decompress_data_with_multi_threads(compressed_data_buf, host, len); + break; + case RAM_SAVE_FLAG_XBZRLE: + host = host_from_stream_offset(f, addr, flags); + if (!host) { + error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + if (load_xbzrle(f, addr, host) < 0) { + error_report("Failed to decompress XBZRLE page at " + RAM_ADDR_FMT, addr); + ret = -EINVAL; + break; + } + break; + case RAM_SAVE_FLAG_EOS: + /* normal exit */ + break; + default: + if (flags & RAM_SAVE_FLAG_HOOK) { + ram_control_load_hook(f, flags); + } else { + error_report("Unknown combination of migration flags: %#x", + flags); + ret = -EINVAL; + } + } + if (!ret) { + ret = qemu_file_get_error(f); + } + } + + rcu_read_unlock(); + DPRINTF("Completed load of VM with exit code %d seq iteration " + "%" PRIu64 "\n", ret, seq_iter); + return ret; +} + +static SaveVMHandlers savevm_ram_handlers = { + .save_live_setup = ram_save_setup, + .save_live_iterate = ram_save_iterate, + .save_live_complete = ram_save_complete, + .save_live_pending = ram_save_pending, + .load_state = ram_load, + .cancel = ram_migration_cancel, +}; + +void ram_mig_init(void) +{ + qemu_mutex_init(&XBZRLE.lock); + register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL); +} +/* Stub function that's gets run on the vcpu when its brought out of the + VM to run inside qemu via async_run_on_cpu()*/ + +static void mig_sleep_cpu(void *opq) +{ + qemu_mutex_unlock_iothread(); + g_usleep(30*1000); + qemu_mutex_lock_iothread(); +} + +/* To reduce the dirty rate explicitly disallow the VCPUs from spending + much time in the VM. The migration thread will try to catchup. + Workload will experience a performance drop. +*/ +static void mig_throttle_guest_down(void) +{ + CPUState *cpu; + + qemu_mutex_lock_iothread(); + CPU_FOREACH(cpu) { + async_run_on_cpu(cpu, mig_sleep_cpu, NULL); + } + qemu_mutex_unlock_iothread(); +} + +static void check_guest_throttling(void) +{ + static int64_t t0; + int64_t t1; + + if (!mig_throttle_on) { + return; + } + + if (!t0) { + t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + return; + } + + t1 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + + /* If it has been more than 40 ms since the last time the guest + * was throttled then do it again. + */ + if (40 < (t1-t0)/1000000) { + mig_throttle_guest_down(); + t0 = t1; + } +} diff --git a/migration/rdma.c b/migration/rdma.c index 77e3444..cf5de7e 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -236,13 +236,13 @@ typedef struct RDMALocalBlock { * corresponding RDMALocalBlock with * the information needed to perform the actual RDMA. */ -typedef struct QEMU_PACKED RDMARemoteBlock { +typedef struct QEMU_PACKED RDMADestBlock { uint64_t remote_host_addr; uint64_t offset; uint64_t length; uint32_t remote_rkey; uint32_t padding; -} RDMARemoteBlock; +} RDMADestBlock; static uint64_t htonll(uint64_t v) { @@ -258,20 +258,20 @@ static uint64_t ntohll(uint64_t v) { return ((uint64_t)ntohl(u.lv[0]) << 32) | (uint64_t) ntohl(u.lv[1]); } -static void remote_block_to_network(RDMARemoteBlock *rb) +static void dest_block_to_network(RDMADestBlock *db) { - rb->remote_host_addr = htonll(rb->remote_host_addr); - rb->offset = htonll(rb->offset); - rb->length = htonll(rb->length); - rb->remote_rkey = htonl(rb->remote_rkey); + db->remote_host_addr = htonll(db->remote_host_addr); + db->offset = htonll(db->offset); + db->length = htonll(db->length); + db->remote_rkey = htonl(db->remote_rkey); } -static void network_to_remote_block(RDMARemoteBlock *rb) +static void network_to_dest_block(RDMADestBlock *db) { - rb->remote_host_addr = ntohll(rb->remote_host_addr); - rb->offset = ntohll(rb->offset); - rb->length = ntohll(rb->length); - rb->remote_rkey = ntohl(rb->remote_rkey); + db->remote_host_addr = ntohll(db->remote_host_addr); + db->offset = ntohll(db->offset); + db->length = ntohll(db->length); + db->remote_rkey = ntohl(db->remote_rkey); } /* @@ -350,7 +350,7 @@ typedef struct RDMAContext { * Description of ram blocks used throughout the code. */ RDMALocalBlocks local_ram_blocks; - RDMARemoteBlock *block; + RDMADestBlock *dest_blocks; /* * Migration on *destination* started. @@ -570,10 +570,10 @@ static int rdma_add_block(RDMAContext *rdma, void *host_addr, * in advanced before the migration starts. This tells us where the RAM blocks * are so that we can register them individually. */ -static void qemu_rdma_init_one_block(void *host_addr, +static int qemu_rdma_init_one_block(const char *block_name, void *host_addr, ram_addr_t block_offset, ram_addr_t length, void *opaque) { - rdma_add_block(opaque, host_addr, block_offset, length); + return rdma_add_block(opaque, host_addr, block_offset, length); } /* @@ -590,7 +590,7 @@ static int qemu_rdma_init_ram_blocks(RDMAContext *rdma) memset(local, 0, sizeof *local); qemu_ram_foreach_block(qemu_rdma_init_one_block, rdma); trace_qemu_rdma_init_ram_blocks(local->nb_blocks); - rdma->block = (RDMARemoteBlock *) g_malloc0(sizeof(RDMARemoteBlock) * + rdma->dest_blocks = (RDMADestBlock *) g_malloc0(sizeof(RDMADestBlock) * rdma->local_ram_blocks.nb_blocks); local->init = true; return 0; @@ -790,6 +790,13 @@ static int qemu_rdma_broken_ipv6_kernel(Error **errp, struct ibv_context *verbs) for (x = 0; x < num_devices; x++) { verbs = ibv_open_device(dev_list[x]); + if (!verbs) { + if (errno == EPERM) { + continue; + } else { + return -EINVAL; + } + } if (ibv_query_port(verbs, 1, &port_attr)) { ibv_close_device(verbs); @@ -2177,8 +2184,8 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) rdma->connected = false; } - g_free(rdma->block); - rdma->block = NULL; + g_free(rdma->dest_blocks); + rdma->dest_blocks = NULL; for (idx = 0; idx < RDMA_WRID_MAX; idx++) { if (rdma->wr_data[idx].control_mr) { @@ -2445,7 +2452,6 @@ static void *qemu_rdma_data_init(const char *host_port, Error **errp) if (host_port) { rdma = g_malloc0(sizeof(RDMAContext)); - memset(rdma, 0, sizeof(RDMAContext)); rdma->current_index = -1; rdma->current_chunk = -1; @@ -2834,7 +2840,7 @@ static int qemu_rdma_accept(RDMAContext *rdma) } } - qemu_set_fd_handler2(rdma->channel->fd, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(rdma->channel->fd, NULL, NULL, NULL); ret = rdma_accept(rdma->cm_id, &conn_param); if (ret) { @@ -2967,25 +2973,25 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque, * their "local" descriptions with what was sent. */ for (i = 0; i < local->nb_blocks; i++) { - rdma->block[i].remote_host_addr = + rdma->dest_blocks[i].remote_host_addr = (uintptr_t)(local->block[i].local_host_addr); if (rdma->pin_all) { - rdma->block[i].remote_rkey = local->block[i].mr->rkey; + rdma->dest_blocks[i].remote_rkey = local->block[i].mr->rkey; } - rdma->block[i].offset = local->block[i].offset; - rdma->block[i].length = local->block[i].length; + rdma->dest_blocks[i].offset = local->block[i].offset; + rdma->dest_blocks[i].length = local->block[i].length; - remote_block_to_network(&rdma->block[i]); + dest_block_to_network(&rdma->dest_blocks[i]); } blocks.len = rdma->local_ram_blocks.nb_blocks - * sizeof(RDMARemoteBlock); + * sizeof(RDMADestBlock); ret = qemu_rdma_post_send_control(rdma, - (uint8_t *) rdma->block, &blocks); + (uint8_t *) rdma->dest_blocks, &blocks); if (ret < 0) { error_report("rdma migration: error sending remote info"); @@ -3141,7 +3147,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, if (flags == RAM_CONTROL_SETUP) { RDMAControlHeader resp = {.type = RDMA_CONTROL_RAM_BLOCKS_RESULT }; RDMALocalBlocks *local = &rdma->local_ram_blocks; - int reg_result_idx, i, j, nb_remote_blocks; + int reg_result_idx, i, j, nb_dest_blocks; head.type = RDMA_CONTROL_RAM_BLOCKS_REQUEST; trace_qemu_rdma_registration_stop_ram(); @@ -3162,7 +3168,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, return ret; } - nb_remote_blocks = resp.len / sizeof(RDMARemoteBlock); + nb_dest_blocks = resp.len / sizeof(RDMADestBlock); /* * The protocol uses two different sets of rkeys (mutually exclusive): @@ -3176,7 +3182,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, * and then propagates the remote ram block descriptions to his local copy. */ - if (local->nb_blocks != nb_remote_blocks) { + if (local->nb_blocks != nb_dest_blocks) { ERROR(errp, "ram blocks mismatch #1! " "Your QEMU command line parameters are probably " "not identical on both the source and destination."); @@ -3184,26 +3190,26 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, } qemu_rdma_move_header(rdma, reg_result_idx, &resp); - memcpy(rdma->block, + memcpy(rdma->dest_blocks, rdma->wr_data[reg_result_idx].control_curr, resp.len); - for (i = 0; i < nb_remote_blocks; i++) { - network_to_remote_block(&rdma->block[i]); + for (i = 0; i < nb_dest_blocks; i++) { + network_to_dest_block(&rdma->dest_blocks[i]); /* search local ram blocks */ for (j = 0; j < local->nb_blocks; j++) { - if (rdma->block[i].offset != local->block[j].offset) { + if (rdma->dest_blocks[i].offset != local->block[j].offset) { continue; } - if (rdma->block[i].length != local->block[j].length) { + if (rdma->dest_blocks[i].length != local->block[j].length) { ERROR(errp, "ram blocks mismatch #2! " "Your QEMU command line parameters are probably " "not identical on both the source and destination."); return -EINVAL; } local->block[j].remote_host_addr = - rdma->block[i].remote_host_addr; - local->block[j].remote_rkey = rdma->block[i].remote_rkey; + rdma->dest_blocks[i].remote_host_addr; + local->block[j].remote_rkey = rdma->dest_blocks[i].remote_rkey; break; } @@ -3331,9 +3337,8 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) trace_rdma_start_incoming_migration_after_rdma_listen(); - qemu_set_fd_handler2(rdma->channel->fd, NULL, - rdma_accept_incoming_migration, NULL, - (void *)(intptr_t) rdma); + qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration, + NULL, (void *)(intptr_t)rdma); return; err: error_propagate(errp, local_err); diff --git a/savevm.c b/migration/savevm.c index 3b0e222..2091882 100644 --- a/savevm.c +++ b/migration/savevm.c @@ -2,6 +2,10 @@ * QEMU System Emulator * * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2009-2015 Red Hat Inc + * + * Authors: + * Juan Quintela <quintela@redhat.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -51,6 +55,8 @@ #define ARP_PTYPE_IP 0x0800 #define ARP_OP_REQUEST_REV 0x3 +static bool skip_section_footers; + static int announce_self_create(uint8_t *buf, uint8_t *mac_addr) { @@ -235,10 +241,15 @@ typedef struct SaveStateEntry { int is_ram; } SaveStateEntry; +typedef struct SaveState { + QTAILQ_HEAD(, SaveStateEntry) handlers; + int global_section_id; +} SaveState; -static QTAILQ_HEAD(savevm_handlers, SaveStateEntry) savevm_handlers = - QTAILQ_HEAD_INITIALIZER(savevm_handlers); -static int global_section_id; +static SaveState savevm_state = { + .handlers = QTAILQ_HEAD_INITIALIZER(savevm_state.handlers), + .global_section_id = 0, +}; static void dump_vmstate_vmsd(FILE *out_file, const VMStateDescription *vmsd, int indent, @@ -263,11 +274,11 @@ static void dump_vmstate_vmsf(FILE *out_file, const VMStateField *field, } static void dump_vmstate_vmss(FILE *out_file, - const VMStateSubsection *subsection, + const VMStateDescription **subsection, int indent) { - if (subsection->vmsd != NULL) { - dump_vmstate_vmsd(out_file, subsection->vmsd, indent, true); + if (*subsection != NULL) { + dump_vmstate_vmsd(out_file, *subsection, indent, true); } } @@ -308,12 +319,12 @@ static void dump_vmstate_vmsd(FILE *out_file, fprintf(out_file, "\n%*s]", indent, ""); } if (vmsd->subsections != NULL) { - const VMStateSubsection *subsection = vmsd->subsections; + const VMStateDescription **subsection = vmsd->subsections; bool first; fprintf(out_file, ",\n%*s\"Subsections\": [\n", indent, ""); first = true; - while (subsection->vmsd != NULL) { + while (*subsection != NULL) { if (!first) { fprintf(out_file, ",\n"); } @@ -383,7 +394,7 @@ static int calculate_new_instance_id(const char *idstr) SaveStateEntry *se; int instance_id = 0; - QTAILQ_FOREACH(se, &savevm_handlers, entry) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (strcmp(idstr, se->idstr) == 0 && instance_id <= se->instance_id) { instance_id = se->instance_id + 1; @@ -397,7 +408,7 @@ static int calculate_compat_instance_id(const char *idstr) SaveStateEntry *se; int instance_id = 0; - QTAILQ_FOREACH(se, &savevm_handlers, entry) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->compat) { continue; } @@ -425,7 +436,7 @@ int register_savevm_live(DeviceState *dev, se = g_malloc0(sizeof(SaveStateEntry)); se->version_id = version_id; - se->section_id = global_section_id++; + se->section_id = savevm_state.global_section_id++; se->ops = ops; se->opaque = opaque; se->vmsd = NULL; @@ -457,7 +468,7 @@ int register_savevm_live(DeviceState *dev, } assert(!se->compat || se->instance_id == 0); /* add at the end of list */ - QTAILQ_INSERT_TAIL(&savevm_handlers, se, entry); + QTAILQ_INSERT_TAIL(&savevm_state.handlers, se, entry); return 0; } @@ -491,9 +502,9 @@ void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque) } pstrcat(id, sizeof(id), idstr); - QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) { + QTAILQ_FOREACH_SAFE(se, &savevm_state.handlers, entry, new_se) { if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) { - QTAILQ_REMOVE(&savevm_handlers, se, entry); + QTAILQ_REMOVE(&savevm_state.handlers, se, entry); if (se->compat) { g_free(se->compat); } @@ -515,7 +526,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, se = g_malloc0(sizeof(SaveStateEntry)); se->version_id = vmsd->version_id; - se->section_id = global_section_id++; + se->section_id = savevm_state.global_section_id++; se->opaque = opaque; se->vmsd = vmsd; se->alias_id = alias_id; @@ -543,7 +554,7 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, } assert(!se->compat || se->instance_id == 0); /* add at the end of list */ - QTAILQ_INSERT_TAIL(&savevm_handlers, se, entry); + QTAILQ_INSERT_TAIL(&savevm_state.handlers, se, entry); return 0; } @@ -552,9 +563,9 @@ void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, { SaveStateEntry *se, *new_se; - QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) { + QTAILQ_FOREACH_SAFE(se, &savevm_state.handlers, entry, new_se) { if (se->vmsd == vmsd && se->opaque == opaque) { - QTAILQ_REMOVE(&savevm_handlers, se, entry); + QTAILQ_REMOVE(&savevm_state.handlers, se, entry); if (se->compat) { g_free(se->compat); } @@ -602,11 +613,84 @@ static void vmstate_save(QEMUFile *f, SaveStateEntry *se, QJSON *vmdesc) vmstate_save_state(f, se->vmsd, se->opaque, vmdesc); } +void savevm_skip_section_footers(void) +{ + skip_section_footers = true; +} + +/* + * Write the header for device section (QEMU_VM_SECTION START/END/PART/FULL) + */ +static void save_section_header(QEMUFile *f, SaveStateEntry *se, + uint8_t section_type) +{ + qemu_put_byte(f, section_type); + qemu_put_be32(f, se->section_id); + + if (section_type == QEMU_VM_SECTION_FULL || + section_type == QEMU_VM_SECTION_START) { + /* ID string */ + size_t len = strlen(se->idstr); + qemu_put_byte(f, len); + qemu_put_buffer(f, (uint8_t *)se->idstr, len); + + qemu_put_be32(f, se->instance_id); + qemu_put_be32(f, se->version_id); + } +} + +/* + * Write a footer onto device sections that catches cases misformatted device + * sections. + */ +static void save_section_footer(QEMUFile *f, SaveStateEntry *se) +{ + if (!skip_section_footers) { + qemu_put_byte(f, QEMU_VM_SECTION_FOOTER); + qemu_put_be32(f, se->section_id); + } +} + +/* + * Read a footer off the wire and check that it matches the expected section + * + * Returns: true if the footer was good + * false if there is a problem (and calls error_report to say why) + */ +static bool check_section_footer(QEMUFile *f, SaveStateEntry *se) +{ + uint8_t read_mark; + uint32_t read_section_id; + + if (skip_section_footers) { + /* No footer to check */ + return true; + } + + read_mark = qemu_get_byte(f); + + if (read_mark != QEMU_VM_SECTION_FOOTER) { + error_report("Missing section footer for %s", se->idstr); + return false; + } + + read_section_id = qemu_get_be32(f); + if (read_section_id != se->section_id) { + error_report("Mismatched section id in footer for %s -" + " read 0x%x expected 0x%x", + se->idstr, read_section_id, se->section_id); + return false; + } + + /* All good */ + return true; +} + bool qemu_savevm_state_blocked(Error **errp) { SaveStateEntry *se; - QTAILQ_FOREACH(se, &savevm_handlers, entry) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->vmsd && se->vmsd->unmigratable) { error_setg(errp, "State blocked by non-migratable device '%s'", se->idstr); @@ -616,6 +700,13 @@ bool qemu_savevm_state_blocked(Error **errp) return false; } +void qemu_savevm_state_header(QEMUFile *f) +{ + trace_savevm_state_header(); + qemu_put_be32(f, QEMU_VM_FILE_MAGIC); + qemu_put_be32(f, QEMU_VM_FILE_VERSION); +} + void qemu_savevm_state_begin(QEMUFile *f, const MigrationParams *params) { @@ -623,19 +714,14 @@ void qemu_savevm_state_begin(QEMUFile *f, int ret; trace_savevm_state_begin(); - QTAILQ_FOREACH(se, &savevm_handlers, entry) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || !se->ops->set_params) { continue; } se->ops->set_params(params, se->opaque); } - qemu_put_be32(f, QEMU_VM_FILE_MAGIC); - qemu_put_be32(f, QEMU_VM_FILE_VERSION); - - QTAILQ_FOREACH(se, &savevm_handlers, entry) { - int len; - + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || !se->ops->save_live_setup) { continue; } @@ -644,19 +730,10 @@ void qemu_savevm_state_begin(QEMUFile *f, continue; } } - /* Section type */ - qemu_put_byte(f, QEMU_VM_SECTION_START); - qemu_put_be32(f, se->section_id); - - /* ID string */ - len = strlen(se->idstr); - qemu_put_byte(f, len); - qemu_put_buffer(f, (uint8_t *)se->idstr, len); - - qemu_put_be32(f, se->instance_id); - qemu_put_be32(f, se->version_id); + save_section_header(f, se, QEMU_VM_SECTION_START); ret = se->ops->save_live_setup(f, se->opaque); + save_section_footer(f, se); if (ret < 0) { qemu_file_set_error(f, ret); break; @@ -676,7 +753,7 @@ int qemu_savevm_state_iterate(QEMUFile *f) int ret = 1; trace_savevm_state_iterate(); - QTAILQ_FOREACH(se, &savevm_handlers, entry) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || !se->ops->save_live_iterate) { continue; } @@ -689,12 +766,12 @@ int qemu_savevm_state_iterate(QEMUFile *f) return 0; } trace_savevm_section_start(se->idstr, se->section_id); - /* Section type */ - qemu_put_byte(f, QEMU_VM_SECTION_PART); - qemu_put_be32(f, se->section_id); + + save_section_header(f, se, QEMU_VM_SECTION_PART); ret = se->ops->save_live_iterate(f, se->opaque); trace_savevm_section_end(se->idstr, se->section_id, ret); + save_section_footer(f, se); if (ret < 0) { qemu_file_set_error(f, ret); @@ -727,7 +804,7 @@ void qemu_savevm_state_complete(QEMUFile *f) cpu_synchronize_all_states(); - QTAILQ_FOREACH(se, &savevm_handlers, entry) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || !se->ops->save_live_complete) { continue; } @@ -737,12 +814,12 @@ void qemu_savevm_state_complete(QEMUFile *f) } } trace_savevm_section_start(se->idstr, se->section_id); - /* Section type */ - qemu_put_byte(f, QEMU_VM_SECTION_END); - qemu_put_be32(f, se->section_id); + + save_section_header(f, se, QEMU_VM_SECTION_END); ret = se->ops->save_live_complete(f, se->opaque); trace_savevm_section_end(se->idstr, se->section_id, ret); + save_section_footer(f, se); if (ret < 0) { qemu_file_set_error(f, ret); return; @@ -752,8 +829,7 @@ void qemu_savevm_state_complete(QEMUFile *f) vmdesc = qjson_new(); json_prop_int(vmdesc, "page_size", TARGET_PAGE_SIZE); json_start_array(vmdesc, "devices"); - QTAILQ_FOREACH(se, &savevm_handlers, entry) { - int len; + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if ((!se->ops || !se->ops->save_state) && !se->vmsd) { continue; @@ -764,22 +840,13 @@ void qemu_savevm_state_complete(QEMUFile *f) json_prop_str(vmdesc, "name", se->idstr); json_prop_int(vmdesc, "instance_id", se->instance_id); - /* Section type */ - qemu_put_byte(f, QEMU_VM_SECTION_FULL); - qemu_put_be32(f, se->section_id); - - /* ID string */ - len = strlen(se->idstr); - qemu_put_byte(f, len); - qemu_put_buffer(f, (uint8_t *)se->idstr, len); - - qemu_put_be32(f, se->instance_id); - qemu_put_be32(f, se->version_id); + save_section_header(f, se, QEMU_VM_SECTION_FULL); vmstate_save(f, se, vmdesc); json_end_object(vmdesc); trace_savevm_section_end(se->idstr, se->section_id, 0); + save_section_footer(f, se); } qemu_put_byte(f, QEMU_VM_EOF); @@ -803,7 +870,7 @@ uint64_t qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size) SaveStateEntry *se; uint64_t ret = 0; - QTAILQ_FOREACH(se, &savevm_handlers, entry) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || !se->ops->save_live_pending) { continue; } @@ -822,7 +889,7 @@ void qemu_savevm_state_cancel(void) SaveStateEntry *se; trace_savevm_state_cancel(); - QTAILQ_FOREACH(se, &savevm_handlers, entry) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->ops && se->ops->cancel) { se->ops->cancel(se->opaque); } @@ -842,6 +909,7 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) } qemu_mutex_unlock_iothread(); + qemu_savevm_state_header(f); qemu_savevm_state_begin(f, ¶ms); qemu_mutex_lock_iothread(); @@ -872,9 +940,7 @@ static int qemu_save_device_state(QEMUFile *f) cpu_synchronize_all_states(); - QTAILQ_FOREACH(se, &savevm_handlers, entry) { - int len; - + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (se->is_ram) { continue; } @@ -882,19 +948,11 @@ static int qemu_save_device_state(QEMUFile *f) continue; } - /* Section type */ - qemu_put_byte(f, QEMU_VM_SECTION_FULL); - qemu_put_be32(f, se->section_id); - - /* ID string */ - len = strlen(se->idstr); - qemu_put_byte(f, len); - qemu_put_buffer(f, (uint8_t *)se->idstr, len); - - qemu_put_be32(f, se->instance_id); - qemu_put_be32(f, se->version_id); + save_section_header(f, se, QEMU_VM_SECTION_FULL); vmstate_save(f, se, NULL); + + save_section_footer(f, se); } qemu_put_byte(f, QEMU_VM_EOF); @@ -906,7 +964,7 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) { SaveStateEntry *se; - QTAILQ_FOREACH(se, &savevm_handlers, entry) { + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!strcmp(se->idstr, idstr) && (instance_id == se->instance_id || instance_id == se->alias_id)) @@ -922,18 +980,26 @@ static SaveStateEntry *find_se(const char *idstr, int instance_id) return NULL; } -typedef struct LoadStateEntry { +struct LoadStateEntry { QLIST_ENTRY(LoadStateEntry) entry; SaveStateEntry *se; int section_id; int version_id; -} LoadStateEntry; +}; -int qemu_loadvm_state(QEMUFile *f) +void loadvm_free_handlers(MigrationIncomingState *mis) { - QLIST_HEAD(, LoadStateEntry) loadvm_handlers = - QLIST_HEAD_INITIALIZER(loadvm_handlers); LoadStateEntry *le, *new_le; + + QLIST_FOREACH_SAFE(le, &mis->loadvm_handlers, entry, new_le) { + QLIST_REMOVE(le, entry); + g_free(le); + } +} + +int qemu_loadvm_state(QEMUFile *f) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; uint8_t section_type; unsigned int v; @@ -964,8 +1030,8 @@ int qemu_loadvm_state(QEMUFile *f) while ((section_type = qemu_get_byte(f)) != QEMU_VM_EOF) { uint32_t instance_id, version_id, section_id; SaveStateEntry *se; - char idstr[257]; - int len; + LoadStateEntry *le; + char idstr[256]; trace_qemu_loadvm_state_section(section_type); switch (section_type) { @@ -973,9 +1039,11 @@ int qemu_loadvm_state(QEMUFile *f) case QEMU_VM_SECTION_FULL: /* Read section start */ section_id = qemu_get_be32(f); - len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)idstr, len); - idstr[len] = 0; + if (!qemu_get_counted_string(f, idstr)) { + error_report("Unable to read ID string for section %u", + section_id); + return -EINVAL; + } instance_id = qemu_get_be32(f); version_id = qemu_get_be32(f); @@ -1004,7 +1072,7 @@ int qemu_loadvm_state(QEMUFile *f) le->se = se; le->section_id = section_id; le->version_id = version_id; - QLIST_INSERT_HEAD(&loadvm_handlers, le, entry); + QLIST_INSERT_HEAD(&mis->loadvm_handlers, le, entry); ret = vmstate_load(f, le->se, le->version_id); if (ret < 0) { @@ -1012,13 +1080,17 @@ int qemu_loadvm_state(QEMUFile *f) " device '%s'", instance_id, idstr); goto out; } + if (!check_section_footer(f, le->se)) { + ret = -EINVAL; + goto out; + } break; case QEMU_VM_SECTION_PART: case QEMU_VM_SECTION_END: section_id = qemu_get_be32(f); trace_qemu_loadvm_state_section_partend(section_id); - QLIST_FOREACH(le, &loadvm_handlers, entry) { + QLIST_FOREACH(le, &mis->loadvm_handlers, entry) { if (le->section_id == section_id) { break; } @@ -1035,6 +1107,10 @@ int qemu_loadvm_state(QEMUFile *f) section_id, le->se->idstr); goto out; } + if (!check_section_footer(f, le->se)) { + ret = -EINVAL; + goto out; + } break; default: error_report("Unknown savevm section type %d", section_type); @@ -1066,11 +1142,6 @@ int qemu_loadvm_state(QEMUFile *f) ret = 0; out: - QLIST_FOREACH_SAFE(le, &loadvm_handlers, entry, new_le) { - QLIST_REMOVE(le, entry); - g_free(le); - } - if (ret == 0) { /* We may not have a VMDESC section, so ignore relative errors */ ret = file_error_after_eof; @@ -1314,9 +1385,11 @@ int load_vmstate(const char *name) } qemu_system_reset(VMRESET_SILENT); + migration_incoming_state_new(f); ret = qemu_loadvm_state(f); qemu_fclose(f); + migration_incoming_state_destroy(); if (ret < 0) { error_report("Error %d while loading VM state", ret); return ret; diff --git a/migration/tcp.c b/migration/tcp.c index 91c9cf3..ae89172 100644 --- a/migration/tcp.c +++ b/migration/tcp.c @@ -65,7 +65,7 @@ static void tcp_accept_incoming_migration(void *opaque) c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen); err = socket_error(); } while (c < 0 && err == EINTR); - qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(s, NULL, NULL, NULL); closesocket(s); DPRINTF("accepted migration\n"); @@ -98,6 +98,6 @@ void tcp_start_incoming_migration(const char *host_port, Error **errp) return; } - qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL, - (void *)(intptr_t)s); + qemu_set_fd_handler(s, tcp_accept_incoming_migration, NULL, + (void *)(intptr_t)s); } diff --git a/migration/unix.c b/migration/unix.c index 1cdadfb..b591813 100644 --- a/migration/unix.c +++ b/migration/unix.c @@ -65,7 +65,7 @@ static void unix_accept_incoming_migration(void *opaque) c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen); err = errno; } while (c < 0 && err == EINTR); - qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(s, NULL, NULL, NULL); close(s); DPRINTF("accepted migration\n"); @@ -98,6 +98,6 @@ void unix_start_incoming_migration(const char *path, Error **errp) return; } - qemu_set_fd_handler2(s, NULL, unix_accept_incoming_migration, NULL, - (void *)(intptr_t)s); + qemu_set_fd_handler(s, unix_accept_incoming_migration, NULL, + (void *)(intptr_t)s); } diff --git a/migration/vmstate.c b/migration/vmstate.c index e5388f0..6138d1a 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -341,11 +341,11 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, } static const VMStateDescription * - vmstate_get_subsection(const VMStateSubsection *sub, char *idstr) +vmstate_get_subsection(const VMStateDescription **sub, char *idstr) { - while (sub && sub->needed) { - if (strcmp(idstr, sub->vmsd->name) == 0) { - return sub->vmsd; + while (sub && *sub && (*sub)->needed) { + if (strcmp(idstr, (*sub)->name) == 0) { + return *sub; } sub++; } @@ -358,7 +358,7 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, trace_vmstate_subsection_load(vmsd->name); while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) { - char idstr[256]; + char idstr[256], *idstr_ret; int ret; uint8_t version_id, len, size; const VMStateDescription *sub_vmsd; @@ -369,11 +369,12 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, trace_vmstate_subsection_load_bad(vmsd->name, "(short)"); return 0; } - size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2); + size = qemu_peek_buffer(f, (uint8_t **)&idstr_ret, len, 2); if (size != len) { trace_vmstate_subsection_load_bad(vmsd->name, "(peek fail)"); return 0; } + memcpy(idstr, idstr_ret, size); idstr[size] = 0; if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) { @@ -405,12 +406,12 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, QJSON *vmdesc) { - const VMStateSubsection *sub = vmsd->subsections; + const VMStateDescription **sub = vmsd->subsections; bool subsection_found = false; - while (sub && sub->needed) { - if (sub->needed(opaque)) { - const VMStateDescription *vmsd = sub->vmsd; + while (sub && *sub && (*sub)->needed) { + if ((*sub)->needed(opaque)) { + const VMStateDescription *vmsd = *sub; uint8_t len; if (vmdesc) { @@ -226,7 +226,6 @@ static mon_cmd_t info_cmds[]; static const mon_cmd_t qmp_cmds[]; Monitor *cur_mon; -Monitor *default_mon; static void monitor_command_cb(void *opaque, const char *cmdline, void *readline_opaque); @@ -2863,6 +2862,34 @@ static mon_cmd_t info_cmds[] = { .mhandler.cmd = hmp_info_memory_devices, }, { + .name = "rocker", + .args_type = "name:s", + .params = "name", + .help = "Show rocker switch", + .mhandler.cmd = hmp_rocker, + }, + { + .name = "rocker-ports", + .args_type = "name:s", + .params = "name", + .help = "Show rocker ports", + .mhandler.cmd = hmp_rocker_ports, + }, + { + .name = "rocker-of-dpa-flows", + .args_type = "name:s,tbl_id:i?", + .params = "name [tbl_id]", + .help = "Show rocker OF-DPA flow tables", + .mhandler.cmd = hmp_rocker_of_dpa_flows, + }, + { + .name = "rocker-of-dpa-groups", + .args_type = "name:s,type:i?", + .params = "name [type]", + .help = "Show rocker OF-DPA groups", + .mhandler.cmd = hmp_rocker_of_dpa_groups, + }, + { .name = NULL, }, }; @@ -5270,9 +5297,6 @@ void monitor_init(CharDriverState *chr, int flags) qemu_mutex_lock(&monitor_lock); QLIST_INSERT_HEAD(&mon_list, mon, entry); qemu_mutex_unlock(&monitor_lock); - - if (!default_mon || (flags & MONITOR_IS_DEFAULT)) - default_mon = mon; } static void bdrv_password_cb(void *opaque, const char *password, diff --git a/net/l2tpv3.c b/net/l2tpv3.c index ed395dc..356dae2 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -133,17 +133,15 @@ typedef struct NetL2TPV3State { } NetL2TPV3State; -static int l2tpv3_can_send(void *opaque); static void net_l2tpv3_send(void *opaque); static void l2tpv3_writable(void *opaque); static void l2tpv3_update_fd_handler(NetL2TPV3State *s) { - qemu_set_fd_handler2(s->fd, - s->read_poll ? l2tpv3_can_send : NULL, - s->read_poll ? net_l2tpv3_send : NULL, - s->write_poll ? l2tpv3_writable : NULL, - s); + qemu_set_fd_handler(s->fd, + s->read_poll ? net_l2tpv3_send : NULL, + s->write_poll ? l2tpv3_writable : NULL, + s); } static void l2tpv3_read_poll(NetL2TPV3State *s, bool enable) @@ -169,13 +167,6 @@ static void l2tpv3_writable(void *opaque) qemu_flush_queued_packets(&s->nc); } -static int l2tpv3_can_send(void *opaque) -{ - NetL2TPV3State *s = opaque; - - return qemu_can_send_packet(&s->nc); -} - static void l2tpv3_send_completed(NetClientState *nc, ssize_t len) { NetL2TPV3State *s = DO_UPCAST(NetL2TPV3State, nc, nc); diff --git a/net/netmap.c b/net/netmap.c index 69300eb..508b829 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -132,26 +132,16 @@ error: return -1; } -/* Tell the event-loop if the netmap backend can send packets - to the frontend. */ -static int netmap_can_send(void *opaque) -{ - NetmapState *s = opaque; - - return qemu_can_send_packet(&s->nc); -} - static void netmap_send(void *opaque); static void netmap_writable(void *opaque); /* Set the event-loop handlers for the netmap backend. */ static void netmap_update_fd_handler(NetmapState *s) { - qemu_set_fd_handler2(s->me.fd, - s->read_poll ? netmap_can_send : NULL, - s->read_poll ? netmap_send : NULL, - s->write_poll ? netmap_writable : NULL, - s); + qemu_set_fd_handler(s->me.fd, + s->read_poll ? netmap_send : NULL, + s->write_poll ? netmap_writable : NULL, + s); } /* Update the read handler. */ @@ -317,7 +307,7 @@ static void netmap_send(void *opaque) /* Keep sending while there are available packets into the netmap RX ring and the forwarding path towards the peer is open. */ - while (!nm_ring_empty(ring) && qemu_can_send_packet(&s->nc)) { + while (!nm_ring_empty(ring)) { uint32_t i; uint32_t idx; bool morefrag; diff --git a/net/socket.c b/net/socket.c index 5a19aa1..c752696 100644 --- a/net/socket.c +++ b/net/socket.c @@ -51,21 +51,12 @@ typedef struct NetSocketState { static void net_socket_accept(void *opaque); static void net_socket_writable(void *opaque); -/* Only read packets from socket when peer can receive them */ -static int net_socket_can_send(void *opaque) -{ - NetSocketState *s = opaque; - - return qemu_can_send_packet(&s->nc); -} - static void net_socket_update_fd_handler(NetSocketState *s) { - qemu_set_fd_handler2(s->fd, - s->read_poll ? net_socket_can_send : NULL, - s->read_poll ? s->send_fn : NULL, - s->write_poll ? net_socket_writable : NULL, - s); + qemu_set_fd_handler(s->fd, + s->read_poll ? s->send_fn : NULL, + s->write_poll ? net_socket_writable : NULL, + s); } static void net_socket_read_poll(NetSocketState *s, bool enable) @@ -142,6 +133,15 @@ static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, return ret; } +static void net_socket_send_completed(NetClientState *nc, ssize_t len) +{ + NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc); + + if (!s->read_poll) { + net_socket_read_poll(s, true); + } +} + static void net_socket_send(void *opaque) { NetSocketState *s = opaque; @@ -211,9 +211,13 @@ static void net_socket_send(void *opaque) buf += l; size -= l; if (s->index >= s->packet_len) { - qemu_send_packet(&s->nc, s->buf, s->packet_len); s->index = 0; s->state = 0; + if (qemu_send_packet_async(&s->nc, s->buf, size, + net_socket_send_completed) == 0) { + net_socket_read_poll(s, false); + break; + } } break; } @@ -234,7 +238,10 @@ static void net_socket_send_dgram(void *opaque) net_socket_write_poll(s, false); return; } - qemu_send_packet(&s->nc, s->buf, size); + if (qemu_send_packet_async(&s->nc, s->buf, size, + net_socket_send_completed) == 0) { + net_socket_read_poll(s, false); + } } static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr) @@ -62,17 +62,15 @@ typedef struct TAPState { static void launch_script(const char *setup_script, const char *ifname, int fd, Error **errp); -static int tap_can_send(void *opaque); static void tap_send(void *opaque); static void tap_writable(void *opaque); static void tap_update_fd_handler(TAPState *s) { - qemu_set_fd_handler2(s->fd, - s->read_poll && s->enabled ? tap_can_send : NULL, - s->read_poll && s->enabled ? tap_send : NULL, - s->write_poll && s->enabled ? tap_writable : NULL, - s); + qemu_set_fd_handler(s->fd, + s->read_poll && s->enabled ? tap_send : NULL, + s->write_poll && s->enabled ? tap_writable : NULL, + s); } static void tap_read_poll(TAPState *s, bool enable) @@ -166,13 +164,6 @@ static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size) return tap_write_packet(s, iov, 1); } -static int tap_can_send(void *opaque) -{ - TAPState *s = opaque; - - return qemu_can_send_packet(&s->nc); -} - #ifndef __sun__ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) { @@ -192,7 +183,7 @@ static void tap_send(void *opaque) int size; int packets = 0; - while (qemu_can_send_packet(&s->nc)) { + while (true) { uint8_t *buf = s->buf; size = tap_read_packet(s->fd, s->buf, sizeof(s->buf)); diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 009bb8d..746603a 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -10,7 +10,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) .PHONY : all clean build-all OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o -CFLAGS += -fPIE -fno-stack-protector -ffreestanding +CFLAGS += -fPIE -fno-stack-protector -ffreestanding -fno-delete-null-pointer-checks LDFLAGS += -Wl,-pie -nostdlib build-all: s390-ccw.img diff --git a/qapi-schema.json b/qapi-schema.json index 6e17a5c..106008c 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3746,10 +3746,14 @@ # # @none: nothing is done # +# @inject-nmi: a non-maskable interrupt is injected into the first VCPU (all +# VCPUS on x86) (since 2.4) +# # Since: 2.1 ## { 'enum': 'WatchdogExpirationAction', - 'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none' ] } + 'data': [ 'reset', 'shutdown', 'poweroff', 'pause', 'debug', 'none', + 'inject-nmi' ] } ## # @IoOperationType @@ -3788,3 +3792,6 @@ # Since: 2.1 ## { 'command': 'rtc-reset-reinjection' } + +# Rocker ethernet network switch +{ 'include': 'qapi/rocker.json' } diff --git a/qapi/block-core.json b/qapi/block-core.json index 8411d4f..afa9d3d 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -259,6 +259,8 @@ # # @iops_size: #optional an I/O size in bytes (Since 1.7) # +# @group: #optional throttle group name (Since 2.4) +# # @cache: the cache mode used for the block device (since: 2.3) # # @write_threshold: configured write threshold for the device. @@ -278,7 +280,7 @@ '*bps_max': 'int', '*bps_rd_max': 'int', '*bps_wr_max': 'int', '*iops_max': 'int', '*iops_rd_max': 'int', '*iops_wr_max': 'int', - '*iops_size': 'int', 'cache': 'BlockdevCacheInfo', + '*iops_size': 'int', '*group': 'str', 'cache': 'BlockdevCacheInfo', 'write_threshold': 'int' } } ## @@ -1062,6 +1064,27 @@ # # Change I/O throttle limits for a block drive. # +# Since QEMU 2.4, each device with I/O limits is member of a throttle +# group. +# +# If two or more devices are members of the same group, the limits +# will apply to the combined I/O of the whole group in a round-robin +# fashion. Therefore, setting new I/O limits to a device will affect +# the whole group. +# +# The name of the group can be specified using the 'group' parameter. +# If the parameter is unset, it is assumed to be the current group of +# that device. If it's not in any group yet, the name of the device +# will be used as the name for its group. +# +# The 'group' parameter can also be used to move a device to a +# different group. In this case the limits specified in the parameters +# will be applied to the new group only. +# +# I/O limits can be disabled by setting all of them to 0. In this case +# the device will be removed from its group and the rest of its +# members will no be affected. The 'group' parameter is ignored. +# # @device: The name of the device # # @bps: total throughput limit in bytes per second @@ -1090,6 +1113,8 @@ # # @iops_size: #optional an I/O size in bytes (Since 1.7) # +# @group: #optional throttle group name (Since 2.4) +# # Returns: Nothing on success # If @device is not a valid block device, DeviceNotFound # @@ -1101,7 +1126,7 @@ '*bps_max': 'int', '*bps_rd_max': 'int', '*bps_wr_max': 'int', '*iops_max': 'int', '*iops_rd_max': 'int', '*iops_wr_max': 'int', - '*iops_size': 'int' } } + '*iops_size': 'int', '*group': 'str' } } ## # @block-stream: diff --git a/qapi/rocker.json b/qapi/rocker.json new file mode 100644 index 0000000..2fe7fdf --- /dev/null +++ b/qapi/rocker.json @@ -0,0 +1,286 @@ +## +# @Rocker: +# +# Rocker switch information. +# +# @name: switch name +# +# @id: switch ID +# +# @ports: number of front-panel ports +# +# Since: 2.4 +## +{ 'struct': 'RockerSwitch', + 'data': { 'name': 'str', 'id': 'uint64', 'ports': 'uint32' } } + +## +# @query-rocker: +# +# Return rocker switch information. +# +# Returns: @Rocker information +# +# Since: 2.4 +## +{ 'command': 'query-rocker', + 'data': { 'name': 'str' }, + 'returns': 'RockerSwitch' } + +## +# @RockerPortDuplex: +# +# An eumeration of port duplex states. +# +# @half: half duplex +# +# @full: full duplex +# +# Since: 2.4 +## +{ 'enum': 'RockerPortDuplex', 'data': [ 'half', 'full' ] } + +## +# @RockerPortAutoneg: +# +# An eumeration of port autoneg states. +# +# @off: autoneg is off +# +# @on: autoneg is on +# +# Since: 2.4 +## +{ 'enum': 'RockerPortAutoneg', 'data': [ 'off', 'on' ] } + +## +# @RockerPort: +# +# Rocker switch port information. +# +# @name: port name +# +# @enabled: port is enabled for I/O +# +# @link-up: physical link is UP on port +# +# @speed: port link speed in Mbps +# +# @duplex: port link duplex +# +# @autoneg: port link autoneg +# +# Since: 2.4 +## +{ 'struct': 'RockerPort', + 'data': { 'name': 'str', 'enabled': 'bool', 'link-up': 'bool', + 'speed': 'uint32', 'duplex': 'RockerPortDuplex', + 'autoneg': 'RockerPortAutoneg' } } + +## +# @query-rocker-ports: +# +# Return rocker switch information. +# +# Returns: @Rocker information +# +# Since: 2.4 +## +{ 'command': 'query-rocker-ports', + 'data': { 'name': 'str' }, + 'returns': ['RockerPort'] } + +## +# @RockerOfDpaFlowKey: +# +# Rocker switch OF-DPA flow key +# +# @priority: key priority, 0 being lowest priority +# +# @tbl-id: flow table ID +# +# @in-pport: #optional physical input port +# +# @tunnel-id: #optional tunnel ID +# +# @vlan-id: #optional VLAN ID +# +# @eth-type: #optional Ethernet header type +# +# @eth-src: #optional Ethernet header source MAC address +# +# @eth-dst: #optional Ethernet header destination MAC address +# +# @ip-proto: #optional IP Header protocol field +# +# @ip-tos: #optional IP header TOS field +# +# @ip-dst: #optional IP header destination address +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the flow key depending if they're relevant to the flow key. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlowKey', + 'data' : { 'priority': 'uint32', 'tbl-id': 'uint32', '*in-pport': 'uint32', + '*tunnel-id': 'uint32', '*vlan-id': 'uint16', + '*eth-type': 'uint16', '*eth-src': 'str', '*eth-dst': 'str', + '*ip-proto': 'uint8', '*ip-tos': 'uint8', '*ip-dst': 'str' } } + +## +# @RockerOfDpaFlowMask: +# +# Rocker switch OF-DPA flow mask +# +# @in-pport: #optional physical input port +# +# @tunnel-id: #optional tunnel ID +# +# @vlan-id: #optional VLAN ID +# +# @eth-src: #optional Ethernet header source MAC address +# +# @eth-dst: #optional Ethernet header destination MAC address +# +# @ip-proto: #optional IP Header protocol field +# +# @ip-tos: #optional IP header TOS field +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the flow mask depending if they're relevant to the flow mask. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlowMask', + 'data' : { '*in-pport': 'uint32', '*tunnel-id': 'uint32', + '*vlan-id': 'uint16', '*eth-src': 'str', '*eth-dst': 'str', + '*ip-proto': 'uint8', '*ip-tos': 'uint8' } } + +## +# @RockerOfDpaFlowAction: +# +# Rocker switch OF-DPA flow action +# +# @goto-tbl: #optional next table ID +# +# @group-id: #optional group ID +# +# @tunnel-lport: #optional tunnel logical port ID +# +# @vlan-id: #optional VLAN ID +# +# @new-vlan-id: #optional new VLAN ID +# +# @out-pport: #optional physical output port +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the flow action depending if they're relevant to the flow action. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlowAction', + 'data' : { '*goto-tbl': 'uint32', '*group-id': 'uint32', + '*tunnel-lport': 'uint32', '*vlan-id': 'uint16', + '*new-vlan-id': 'uint16', '*out-pport': 'uint32' } } + +## +# @RockerOfDpaFlow: +# +# Rocker switch OF-DPA flow +# +# @cookie: flow unique cookie ID +# +# @hits: count of matches (hits) on flow +# +# @key: flow key +# +# @mask: flow mask +# +# @action: flow action +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlow', + 'data': { 'cookie': 'uint64', 'hits': 'uint64', 'key': 'RockerOfDpaFlowKey', + 'mask': 'RockerOfDpaFlowMask', 'action': 'RockerOfDpaFlowAction' } } + +## +# @query-rocker-of-dpa-flows: +# +# Return rocker OF-DPA flow information. +# +# @name: switch name +# +# @tbl-id: #optional flow table ID. If tbl-id is not specified, returns +# flow information for all tables. +# +# Returns: @Rocker OF-DPA flow information +# +# Since: 2.4 +## +{ 'command': 'query-rocker-of-dpa-flows', + 'data': { 'name': 'str', '*tbl-id': 'uint32' }, + 'returns': ['RockerOfDpaFlow'] } + +## +# @RockerOfDpaGroup: +# +# Rocker switch OF-DPA group +# +# @id: group unique ID +# +# @type: group type +# +# @vlan-id: #optional VLAN ID +# +# @pport: #optional physical port number +# +# @index: #optional group index, unique with group type +# +# @out-pport: #optional output physical port number +# +# @group-id: #optional next group ID +# +# @set-vlan-id: #optional VLAN ID to set +# +# @pop-vlan: #optional pop VLAN headr from packet +# +# @group-ids: #optional list of next group IDs +# +# @set-eth-src: #optional set source MAC address in Ethernet header +# +# @set-eth-dst: #optional set destination MAC address in Ethernet header +# +# @ttl-check: #optional perform TTL check +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the group depending if they're relevant to the group type. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaGroup', + 'data': { 'id': 'uint32', 'type': 'uint8', '*vlan-id': 'uint16', + '*pport': 'uint32', '*index': 'uint32', '*out-pport': 'uint32', + '*group-id': 'uint32', '*set-vlan-id': 'uint16', + '*pop-vlan': 'uint8', '*group-ids': ['uint32'], + '*set-eth-src': 'str', '*set-eth-dst': 'str', + '*ttl-check': 'uint8' } } + +## +# @query-rocker-of-dpa-groups: +# +# Return rocker OF-DPA group information. +# +# @name: switch name +# +# @type: #optional group type. If type is not specified, returns +# group information for all group types. +# +# Returns: @Rocker OF-DPA group information +# +# Since: 2.4 +## +{ 'command': 'query-rocker-of-dpa-groups', + 'data': { 'name': 'str', '*type': 'uint8' }, + 'returns': ['RockerOfDpaGroup'] } diff --git a/qemu-options.hx b/qemu-options.hx index c6221d4..5438f98 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -468,6 +468,7 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive, " [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n" " [[,iops_max=im]|[[,iops_rd_max=irm][,iops_wr_max=iwm]]]\n" " [[,iops_size=is]]\n" + " [[,group=g]]\n" " use 'file' as a drive image\n", QEMU_ARCH_ALL) STEXI @item -drive @var{option}[,@var{option}[,@var{option}[,...]]] @@ -3154,7 +3155,7 @@ when the shift value is high (how high depends on the host machine). ETEXI DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \ - "-watchdog i6300esb|ib700\n" \ + "-watchdog model\n" \ " enable virtual hardware watchdog [default=none]\n", QEMU_ARCH_ALL) STEXI @@ -3162,16 +3163,24 @@ STEXI @findex -watchdog Create a virtual hardware watchdog device. Once enabled (by a guest action), the watchdog must be periodically polled by an agent inside -the guest or else the guest will be restarted. +the guest or else the guest will be restarted. Choose a model for +which your guest has drivers. -The @var{model} is the model of hardware watchdog to emulate. Choices -for model are: @code{ib700} (iBASE 700) which is a very simple ISA -watchdog with a single timer, or @code{i6300esb} (Intel 6300ESB I/O -controller hub) which is a much more featureful PCI-based dual-timer -watchdog. Choose a model for which your guest has drivers. - -Use @code{-watchdog help} to list available hardware models. Only one +The @var{model} is the model of hardware watchdog to emulate. Use +@code{-watchdog help} to list available hardware models. Only one watchdog can be enabled for a guest. + +The following models may be available: +@table @option +@item ib700 +iBASE 700 is a very simple ISA watchdog with a single timer. +@item i6300esb +Intel 6300ESB I/O controller hub is a much more featureful PCI-based +dual-timer watchdog. +@item diag288 +A virtual watchdog for s390x backed by the diagnose 288 hypercall +(currently KVM only). +@end table ETEXI DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \ diff --git a/qmp-commands.hx b/qmp-commands.hx index 867a21f..1db6524 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1853,7 +1853,7 @@ EQMP { .name = "block_set_io_throttle", - .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?", + .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?,group:s?", .mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle, }, @@ -1879,6 +1879,7 @@ Arguments: - "iops_rd_max": read I/O operations max (json-int) - "iops_wr_max": write I/O operations max (json-int) - "iops_size": I/O size in bytes when limiting (json-int) +- "group": throttle group name (json-string) Example: @@ -4165,3 +4166,106 @@ Example: <- { "return": {} } EQMP + + { + .name = "query-rocker", + .args_type = "name:s", + .mhandler.cmd_new = qmp_marshal_input_query_rocker, + }, + +SQMP +Show rocker switch +------------------ + +Arguments: + +- "name": switch name + +Example: + +-> { "execute": "query-rocker", "arguments": { "name": "sw1" } } +<- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}} + +EQMP + + { + .name = "query-rocker-ports", + .args_type = "name:s", + .mhandler.cmd_new = qmp_marshal_input_query_rocker_ports, + }, + +SQMP +Show rocker switch ports +------------------------ + +Arguments: + +- "name": switch name + +Example: + +-> { "execute": "query-rocker-ports", "arguments": { "name": "sw1" } } +<- { "return": [ {"duplex": "full", "enabled": true, "name": "sw1.1", + "autoneg": "off", "link-up": true, "speed": 10000}, + {"duplex": "full", "enabled": true, "name": "sw1.2", + "autoneg": "off", "link-up": true, "speed": 10000} + ]} + +EQMP + + { + .name = "query-rocker-of-dpa-flows", + .args_type = "name:s,tbl-id:i?", + .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_flows, + }, + +SQMP +Show rocker switch OF-DPA flow tables +------------------------------------- + +Arguments: + +- "name": switch name +- "tbl-id": (optional) flow table ID + +Example: + +-> { "execute": "query-rocker-of-dpa-flows", "arguments": { "name": "sw1" } } +<- { "return": [ {"key": {"in-pport": 0, "priority": 1, "tbl-id": 0}, + "hits": 138, + "cookie": 0, + "action": {"goto-tbl": 10}, + "mask": {"in-pport": 4294901760} + }, + {...more...}, + ]} + +EQMP + + { + .name = "query-rocker-of-dpa-groups", + .args_type = "name:s,type:i?", + .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_groups, + }, + +SQMP +Show rocker OF-DPA group tables +------------------------------- + +Arguments: + +- "name": switch name +- "type": (optional) group type + +Example: + +-> { "execute": "query-rocker-of-dpa-groups", "arguments": { "name": "sw1" } } +<- { "return": [ {"type": 0, "out-pport": 2, "pport": 2, "vlan-id": 3841, + "pop-vlan": 1, "id": 251723778}, + {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3841, + "pop-vlan": 1, "id": 251723776}, + {"type": 0, "out-pport": 1, "pport": 1, "vlan-id": 3840, + "pop-vlan": 1, "id": 251658241}, + {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3840, + "pop-vlan": 1, "id": 251658240} + ]} diff --git a/qobject/qdict.c b/qobject/qdict.c index ea239f0..190791b 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -477,6 +477,39 @@ static void qdict_destroy_obj(QObject *obj) g_free(qdict); } +/** + * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the + * value of 'key' in 'src' is copied there (and the refcount increased + * accordingly). + */ +void qdict_copy_default(QDict *dst, QDict *src, const char *key) +{ + QObject *val; + + if (qdict_haskey(dst, key)) { + return; + } + + val = qdict_get(src, key); + if (val) { + qobject_incref(val); + qdict_put_obj(dst, key, val); + } +} + +/** + * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a + * new QString initialised by 'val' is put there. + */ +void qdict_set_default_str(QDict *dst, const char *key, const char *val) +{ + if (qdict_haskey(dst, key)) { + return; + } + + qdict_put(dst, key, qstring_from_str(val)); +} + static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix); @@ -597,17 +630,21 @@ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) } } -static bool qdict_has_prefixed_entries(const QDict *src, const char *start) +static int qdict_count_prefixed_entries(const QDict *src, const char *start) { const QDictEntry *entry; + int count = 0; for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { if (strstart(entry->key, start, NULL)) { - return true; + if (count == INT_MAX) { + return -ERANGE; + } + count++; } } - return false; + return count; } /** @@ -646,7 +683,8 @@ void qdict_array_split(QDict *src, QList **dst) snprintf_ret = snprintf(prefix, 32, "%u.", i); assert(snprintf_ret < 32); - is_subqdict = qdict_has_prefixed_entries(src, prefix); + /* Overflow is the same as positive non-zero results */ + is_subqdict = qdict_count_prefixed_entries(src, prefix); // There may be either a single subordinate object (named "%u") or // multiple objects (each with a key prefixed "%u."), but not both. @@ -667,6 +705,71 @@ void qdict_array_split(QDict *src, QList **dst) } /** + * qdict_array_entries(): Returns the number of direct array entries if the + * sub-QDict of src specified by the prefix in subqdict (or src itself for + * prefix == "") is valid as an array, i.e. the length of the created list if + * the sub-QDict would become empty after calling qdict_array_split() on it. If + * the array is not valid, -EINVAL is returned. + */ +int qdict_array_entries(QDict *src, const char *subqdict) +{ + const QDictEntry *entry; + unsigned i; + unsigned entries = 0; + size_t subqdict_len = strlen(subqdict); + + assert(!subqdict_len || subqdict[subqdict_len - 1] == '.'); + + /* qdict_array_split() loops until UINT_MAX, but as we want to return + * negative errors, we only have a signed return value here. Any additional + * entries will lead to -EINVAL. */ + for (i = 0; i < INT_MAX; i++) { + QObject *subqobj; + int subqdict_entries; + size_t slen = 32 + subqdict_len; + char indexstr[slen], prefix[slen]; + size_t snprintf_ret; + + snprintf_ret = snprintf(indexstr, slen, "%s%u", subqdict, i); + assert(snprintf_ret < slen); + + subqobj = qdict_get(src, indexstr); + + snprintf_ret = snprintf(prefix, slen, "%s%u.", subqdict, i); + assert(snprintf_ret < slen); + + subqdict_entries = qdict_count_prefixed_entries(src, prefix); + if (subqdict_entries < 0) { + return subqdict_entries; + } + + /* There may be either a single subordinate object (named "%u") or + * multiple objects (each with a key prefixed "%u."), but not both. */ + if (subqobj && subqdict_entries) { + return -EINVAL; + } else if (!subqobj && !subqdict_entries) { + break; + } + + entries += subqdict_entries ? subqdict_entries : 1; + } + + /* Consider everything handled that isn't part of the given sub-QDict */ + for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { + if (!strstart(qdict_entry_key(entry), subqdict, NULL)) { + entries++; + } + } + + /* Anything left in the sub-QDict that wasn't handled? */ + if (qdict_size(src) != entries) { + return -EINVAL; + } + + return i; +} + +/** * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all * elements from src to dest. * diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index 0c8b22f..f6894be 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -474,6 +474,7 @@ class MigrationDump(object): QEMU_VM_SECTION_FULL = 0x04 QEMU_VM_SUBSECTION = 0x05 QEMU_VM_VMDESCRIPTION = 0x06 + QEMU_VM_SECTION_FOOTER= 0x7e def __init__(self, filename): self.section_classes = { ( 'ram', 0 ) : [ RamSection, None ], @@ -526,6 +527,10 @@ class MigrationDump(object): elif section_type == self.QEMU_VM_SECTION_PART or section_type == self.QEMU_VM_SECTION_END: section_id = file.read32() self.sections[section_id].read() + elif section_type == self.QEMU_VM_SECTION_FOOTER: + read_section_id = file.read32() + if read_section_id != section_id: + raise Exception("Mismatched section footer: %x vs %x" % (read_section_id, section_id)) else: raise Exception("Unknown section type: %d" % section_type) file.close() diff --git a/softmmu_template.h b/softmmu_template.h index 39f571b..d42d89d 100644 --- a/softmmu_template.h +++ b/softmmu_template.h @@ -548,6 +548,28 @@ glue(glue(helper_st, SUFFIX), MMUSUFFIX)(CPUArchState *env, target_ulong addr, helper_te_st_name(env, addr, val, oi, GETRA()); } +#if DATA_SIZE == 1 +/* Probe for whether the specified guest write access is permitted. + * If it is not permitted then an exception will be taken in the same + * way as if this were a real write access (and we will not return). + * Otherwise the function will return, and there will be a valid + * entry in the TLB for this access. + */ +void probe_write(CPUArchState *env, target_ulong addr, int mmu_idx, + uintptr_t retaddr) +{ + int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; + + if ((addr & TARGET_PAGE_MASK) + != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + /* TLB entry is for a different page */ + if (!VICTIM_TLB_HIT(addr_write)) { + tlb_fill(ENV_GET_CPU(env), addr, MMU_DATA_STORE, mmu_idx, retaddr); + } + } +} +#endif #endif /* !defined(SOFTMMU_CODE_ACCESS) */ #undef READ_ACCESS_TYPE diff --git a/stubs/set-fd-handler.c b/stubs/set-fd-handler.c index fc874d3..a8481bc 100644 --- a/stubs/set-fd-handler.c +++ b/stubs/set-fd-handler.c @@ -1,8 +1,7 @@ #include "qemu-common.h" #include "qemu/main-loop.h" -int qemu_set_fd_handler2(int fd, - IOCanReadHandler *fd_read_poll, +void qemu_set_fd_handler(int fd, IOHandler *fd_read, IOHandler *fd_write, void *opaque) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index ed5a644..072aa9b 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -103,6 +103,9 @@ typedef struct ARMCPU { /* CPU has security extension */ bool has_el3; + /* CPU has memory protection unit */ + bool has_mpu; + /* PSCI conduit used to invoke PSCI methods * 0 - disabled, 1 - smc, 2 - hvc */ @@ -116,6 +119,9 @@ typedef struct ARMCPU { /* KVM init features for this CPU */ uint32_t kvm_init_features[7]; + /* Uniprocessor system with MP extensions */ + bool mp_is_up; + /* The instance init functions for implementation-specific subclasses * set these fields to specify the implementation-dependent values of * various constant registers and reset values of non-constant @@ -127,6 +133,7 @@ typedef struct ARMCPU { * prefix means a constant register. */ uint32_t midr; + uint32_t revidr; uint32_t reset_fpsid; uint32_t mvfr0; uint32_t mvfr1; @@ -159,6 +166,7 @@ typedef struct ARMCPU { uint64_t id_aa64mmfr1; uint32_t dbgdidr; uint32_t clidr; + uint64_t mp_affinity; /* MP ID without feature bits */ /* The elements of this array are the CCSIDR values for each cache, * in the order L1DCache, L1ICache, L2DCache, L2ICache, etc. */ diff --git a/target-arm/cpu.c b/target-arm/cpu.c index 4a888ab..7496983 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -383,17 +383,29 @@ static inline void unset_feature(CPUARMState *env, int feature) env->features &= ~(1ULL << feature); } +#define ARM_CPUS_PER_CLUSTER 8 + static void arm_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); ARMCPU *cpu = ARM_CPU(obj); static bool inited; + uint32_t Aff1, Aff0; cs->env_ptr = &cpu->env; cpu_exec_init(&cpu->env); cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); + /* This cpu-id-to-MPIDR affinity is used only for TCG; KVM will override it. + * We don't support setting cluster ID ([16..23]) (known as Aff2 + * in later ARM ARM versions), or any of the higher affinity level fields, + * so these bits always RAZ. + */ + Aff1 = cs->cpu_index / ARM_CPUS_PER_CLUSTER; + Aff0 = cs->cpu_index % ARM_CPUS_PER_CLUSTER; + cpu->mp_affinity = (Aff1 << 8) | Aff0; + #ifndef CONFIG_USER_ONLY /* Our inbound IRQ and FIQ lines */ if (kvm_enabled()) { @@ -442,6 +454,9 @@ static Property arm_cpu_rvbar_property = static Property arm_cpu_has_el3_property = DEFINE_PROP_BOOL("has_el3", ARMCPU, has_el3, true); +static Property arm_cpu_has_mpu_property = + DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true); + static void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -469,6 +484,12 @@ static void arm_cpu_post_init(Object *obj) qdev_property_add_static(DEVICE(obj), &arm_cpu_has_el3_property, &error_abort); } + + if (arm_feature(&cpu->env, ARM_FEATURE_MPU)) { + qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property, + &error_abort); + } + } static void arm_cpu_finalizefn(Object *obj) @@ -533,6 +554,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) if (arm_feature(env, ARM_FEATURE_CBAR_RO)) { set_feature(env, ARM_FEATURE_CBAR); } + if (arm_feature(env, ARM_FEATURE_THUMB2) && + !arm_feature(env, ARM_FEATURE_M)) { + set_feature(env, ARM_FEATURE_THUMB_DSP); + } if (cpu->reset_hivecs) { cpu->reset_sctlr |= (1 << 13); @@ -551,6 +576,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->id_aa64pfr0 &= ~0xf000; } + if (!cpu->has_mpu) { + unset_feature(env, ARM_FEATURE_MPU); + } + register_cp_regs_for_features(cpu); arm_cpu_register_gdb_regs_for_features(cpu); diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 21b5b8e..c9d2330 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -384,7 +384,6 @@ typedef struct CPUARMState { uint32_t control; int current_sp; int exception; - int pending_exception; } v7m; /* Information associated with an exception about to be taken: @@ -890,6 +889,7 @@ enum arm_features { ARM_FEATURE_V8_SHA1, /* implements SHA1 part of v8 Crypto Extensions */ ARM_FEATURE_V8_SHA256, /* implements SHA256 part of v8 Crypto Extensions */ ARM_FEATURE_V8_PMULL, /* implements PMULL part of v8 Crypto Extensions */ + ARM_FEATURE_THUMB_DSP, /* DSP insns supported in the Thumb encodings */ }; static inline int arm_feature(CPUARMState *env, int feature) diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index bf7dd68..63c8b1c 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -110,6 +110,7 @@ static void aarch64_a57_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_CRC); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A57; cpu->midr = 0x411fd070; + cpu->revidr = 0x00000000; cpu->reset_fpsid = 0x41034070; cpu->mvfr0 = 0x10110222; cpu->mvfr1 = 0x12111111; @@ -159,7 +160,9 @@ static void aarch64_a53_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); + cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A53; cpu->midr = 0x410fd034; + cpu->revidr = 0x00000000; cpu->reset_fpsid = 0x41034070; cpu->mvfr0 = 0x10110222; cpu->mvfr1 = 0x12111111; diff --git a/target-arm/helper.c b/target-arm/helper.c index 3da0c05..00509b1 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -12,10 +12,10 @@ #include <zlib.h> /* For crc32 */ #ifndef CONFIG_USER_ONLY -static inline int get_phys_addr(CPUARMState *env, target_ulong address, - int access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, - target_ulong *page_size); +static inline bool get_phys_addr(CPUARMState *env, target_ulong address, + int access_type, ARMMMUIdx mmu_idx, + hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, + target_ulong *page_size, uint32_t *fsr); /* Definitions for the PMCCNTR and PMCR registers */ #define PMCRD 0x8 @@ -1495,19 +1495,20 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, hwaddr phys_addr; target_ulong page_size; int prot; - int ret; + uint32_t fsr; + bool ret; uint64_t par64; MemTxAttrs attrs = {}; ret = get_phys_addr(env, value, access_type, mmu_idx, - &phys_addr, &attrs, &prot, &page_size); + &phys_addr, &attrs, &prot, &page_size, &fsr); if (extended_addresses_enabled(env)) { - /* ret is a DFSR/IFSR value for the long descriptor + /* fsr is a DFSR/IFSR value for the long descriptor * translation table format, but with WnR always clear. * Convert it to a 64-bit PAR. */ par64 = (1 << 11); /* LPAE bit always set */ - if (ret == 0) { + if (!ret) { par64 |= phys_addr & ~0xfffULL; if (!attrs.secure) { par64 |= (1 << 9); /* NS */ @@ -1515,18 +1516,18 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, /* We don't set the ATTR or SH fields in the PAR. */ } else { par64 |= 1; /* F */ - par64 |= (ret & 0x3f) << 1; /* FS */ + par64 |= (fsr & 0x3f) << 1; /* FS */ /* Note that S2WLK and FSTAGE are always zero, because we don't * implement virtualization and therefore there can't be a stage 2 * fault. */ } } else { - /* ret is a DFSR/IFSR value for the short descriptor + /* fsr is a DFSR/IFSR value for the short descriptor * translation table format (with WnR always clear). * Convert it to a 32-bit PAR. */ - if (ret == 0) { + if (!ret) { /* We do not set any attribute bits in the PAR */ if (page_size == (1 << 24) && arm_feature(env, ARM_FEATURE_V7)) { @@ -1538,8 +1539,8 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, par64 |= (1 << 9); /* NS */ } } else { - par64 = ((ret & (1 << 10)) >> 5) | ((ret & (1 << 12)) >> 6) | - ((ret & 0xf) << 1) | 1; + par64 = ((fsr & (1 << 10)) >> 5) | ((fsr & (1 << 12)) >> 6) | + ((fsr & 0xf) << 1) | 1; } } return par64; @@ -1846,7 +1847,7 @@ static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, raw_write(env, ri, value); } -static const ARMCPRegInfo vmsa_cp_reginfo[] = { +static const ARMCPRegInfo vmsa_pmsa_cp_reginfo[] = { { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_ALIAS, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.dfsr_s), @@ -1856,6 +1857,18 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .access = PL1_RW, .resetvalue = 0, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.ifsr_s), offsetoflow32(CPUARMState, cp15.ifsr_ns) } }, + { .name = "DFAR", .cp = 15, .opc1 = 0, .crn = 6, .crm = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, + .bank_fieldoffsets = { offsetof(CPUARMState, cp15.dfar_s), + offsetof(CPUARMState, cp15.dfar_ns) } }, + { .name = "FAR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), + .resetvalue = 0, }, + REGINFO_SENTINEL +}; + +static const ARMCPRegInfo vmsa_cp_reginfo[] = { { .name = "ESR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .crn = 5, .crm = 2, .opc1 = 0, .opc2 = 0, .access = PL1_RW, @@ -1880,14 +1893,6 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { .resetfn = arm_cp_reset_ignore, .raw_writefn = vmsa_ttbcr_raw_write, .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.tcr_el[3]), offsetoflow32(CPUARMState, cp15.tcr_el[1])} }, - { .name = "FAR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[1]), - .resetvalue = 0, }, - { .name = "DFAR", .cp = 15, .opc1 = 0, .crn = 6, .crm = 0, .opc2 = 0, - .access = PL1_RW, .resetvalue = 0, - .bank_fieldoffsets = { offsetof(CPUARMState, cp15.dfar_s), - offsetof(CPUARMState, cp15.dfar_ns) } }, REGINFO_SENTINEL }; @@ -2063,19 +2068,18 @@ static const ARMCPRegInfo strongarm_cp_reginfo[] = { static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) { - CPUState *cs = CPU(arm_env_get_cpu(env)); - uint32_t mpidr = cs->cpu_index; - /* We don't support setting cluster ID ([8..11]) (known as Aff1 - * in later ARM ARM versions), or any of the higher affinity level fields, - * so these bits always RAZ. - */ + ARMCPU *cpu = ARM_CPU(arm_env_get_cpu(env)); + uint64_t mpidr = cpu->mp_affinity; + if (arm_feature(env, ARM_FEATURE_V7MP)) { mpidr |= (1U << 31); /* Cores which are uniprocessor (non-coherent) * but still implement the MP extensions set - * bit 30. (For instance, A9UP.) However we do - * not currently model any of those cores. + * bit 30. (For instance, Cortex-R5). */ + if (cpu->mp_is_up) { + mpidr |= (1u << 30); + } } return mpidr; } @@ -3196,7 +3200,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_V6K)) { define_arm_cp_regs(cpu, v6k_cp_reginfo); } - if (arm_feature(env, ARM_FEATURE_V7MP)) { + if (arm_feature(env, ARM_FEATURE_V7MP) && + !arm_feature(env, ARM_FEATURE_MPU)) { define_arm_cp_regs(cpu, v7mp_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_V7)) { @@ -3348,6 +3353,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) assert(!arm_feature(env, ARM_FEATURE_V6)); define_arm_cp_regs(cpu, pmsav5_cp_reginfo); } else { + define_arm_cp_regs(cpu, vmsa_pmsa_cp_reginfo); define_arm_cp_regs(cpu, vmsa_cp_reginfo); } if (arm_feature(env, ARM_FEATURE_THUMB2EE)) { @@ -3423,16 +3429,19 @@ void register_cp_regs_for_features(ARMCPU *cpu) REGINFO_SENTINEL }; ARMCPRegInfo id_v8_midr_cp_reginfo[] = { - /* v8 MIDR -- the wildcard isn't necessary, and nor is the - * variable-MIDR TI925 behaviour. Instead we have a single - * (strictly speaking IMPDEF) alias of the MIDR, REVIDR. - */ { .name = "MIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->midr }, + /* crn = 0 op1 = 0 crm = 0 op2 = 4,7 : AArch32 aliases of MIDR */ + { .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST, + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 4, + .access = PL1_R, .resetvalue = cpu->midr }, + { .name = "MIDR", .type = ARM_CP_ALIAS | ARM_CP_CONST, + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 7, + .access = PL1_R, .resetvalue = cpu->midr }, { .name = "REVIDR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 6, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->midr }, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->revidr }, REGINFO_SENTINEL }; ARMCPRegInfo id_cp_reginfo[] = { @@ -3448,11 +3457,14 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "TCMTR", .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "TLBTR", - .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, REGINFO_SENTINEL }; + /* TLBTR is specific to VMSA */ + ARMCPRegInfo id_tlbtr_reginfo = { + .name = "TLBTR", + .cp = 15, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 3, + .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0, + }; ARMCPRegInfo crn0_wi_reginfo = { .name = "CRN0_WI", .cp = 15, .crn = 0, .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W, @@ -3474,6 +3486,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) for (r = id_cp_reginfo; r->type != ARM_CP_SENTINEL; r++) { r->access = PL1_RW; } + id_tlbtr_reginfo.access = PL1_RW; } if (arm_feature(env, ARM_FEATURE_V8)) { define_arm_cp_regs(cpu, id_v8_midr_cp_reginfo); @@ -3481,6 +3494,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, id_pre_v8_midr_cp_reginfo); } define_arm_cp_regs(cpu, id_cp_reginfo); + if (!arm_feature(env, ARM_FEATURE_MPU)) { + define_one_arm_cp_reg(cpu, &id_tlbtr_reginfo); + } } if (arm_feature(env, ARM_FEATURE_MPIDR)) { @@ -5229,9 +5245,10 @@ static uint64_t arm_ldq_ptw(CPUState *cs, hwaddr addr, bool is_secure) return address_space_ldq(cs->as, addr, attrs, NULL); } -static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, - ARMMMUIdx mmu_idx, hwaddr *phys_ptr, - int *prot, target_ulong *page_size) +static bool get_phys_addr_v5(CPUARMState *env, uint32_t address, + int access_type, ARMMMUIdx mmu_idx, + hwaddr *phys_ptr, int *prot, + target_ulong *page_size, uint32_t *fsr) { CPUState *cs = CPU(arm_env_get_cpu(env)); int code; @@ -5302,20 +5319,25 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, ap = (desc >> (4 + ((address >> 9) & 6))) & 3; *page_size = 0x1000; break; - case 3: /* 1k page. */ + case 3: /* 1k page, or ARMv6/XScale "extended small (4k) page" */ if (type == 1) { - if (arm_feature(env, ARM_FEATURE_XSCALE)) { + /* ARMv6/XScale extended small page format */ + if (arm_feature(env, ARM_FEATURE_XSCALE) + || arm_feature(env, ARM_FEATURE_V6)) { phys_addr = (desc & 0xfffff000) | (address & 0xfff); + *page_size = 0x1000; } else { - /* Page translation fault. */ + /* UNPREDICTABLE in ARMv5; we choose to take a + * page translation fault. + */ code = 7; goto do_fault; } } else { phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); + *page_size = 0x400; } ap = (desc >> 4) & 3; - *page_size = 0x400; break; default: /* Never happens, but compiler isn't smart enough to tell. */ @@ -5330,15 +5352,16 @@ static int get_phys_addr_v5(CPUARMState *env, uint32_t address, int access_type, goto do_fault; } *phys_ptr = phys_addr; - return 0; + return false; do_fault: - return code | (domain << 4); + *fsr = code | (domain << 4); + return true; } -static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, - ARMMMUIdx mmu_idx, hwaddr *phys_ptr, - MemTxAttrs *attrs, - int *prot, target_ulong *page_size) +static bool get_phys_addr_v6(CPUARMState *env, uint32_t address, + int access_type, ARMMMUIdx mmu_idx, + hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, + target_ulong *page_size, uint32_t *fsr) { CPUState *cs = CPU(arm_env_get_cpu(env)); int code; @@ -5392,6 +5415,8 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, if (desc & (1 << 18)) { /* Supersection. */ phys_addr = (desc & 0xff000000) | (address & 0x00ffffff); + phys_addr |= (uint64_t)extract32(desc, 20, 4) << 32; + phys_addr |= (uint64_t)extract32(desc, 5, 4) << 36; *page_size = 0x1000000; } else { /* Section. */ @@ -5469,9 +5494,10 @@ static int get_phys_addr_v6(CPUARMState *env, uint32_t address, int access_type, attrs->secure = false; } *phys_ptr = phys_addr; - return 0; + return false; do_fault: - return code | (domain << 4); + *fsr = code | (domain << 4); + return true; } /* Fault type for long-descriptor MMU fault reporting; this corresponds @@ -5483,10 +5509,10 @@ typedef enum { permission_fault = 3, } MMUFaultType; -static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, - int access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot, - target_ulong *page_size_ptr) +static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, + int access_type, ARMMMUIdx mmu_idx, + hwaddr *phys_ptr, MemTxAttrs *txattrs, int *prot, + target_ulong *page_size_ptr, uint32_t *fsr) { CPUState *cs = CPU(arm_env_get_cpu(env)); /* Read an LPAE long-descriptor translation table. */ @@ -5725,16 +5751,17 @@ static int get_phys_addr_lpae(CPUARMState *env, target_ulong address, } *phys_ptr = descaddr; *page_size_ptr = page_size; - return 0; + return false; do_fault: /* Long-descriptor format IFSR/DFSR value */ - return (1 << 9) | (fault_type << 2) | level; + *fsr = (1 << 9) | (fault_type << 2) | level; + return true; } -static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, - int access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, int *prot) +static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address, + int access_type, ARMMMUIdx mmu_idx, + hwaddr *phys_ptr, int *prot, uint32_t *fsr) { int n; uint32_t mask; @@ -5756,7 +5783,8 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, } } if (n < 0) { - return 2; + *fsr = 2; + return true; } if (access_type == 2) { @@ -5767,10 +5795,12 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, mask = (mask >> (n * 4)) & 0xf; switch (mask) { case 0: - return 1; + *fsr = 1; + return true; case 1: if (is_user) { - return 1; + *fsr = 1; + return true; } *prot = PAGE_READ | PAGE_WRITE; break; @@ -5785,7 +5815,8 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, break; case 5: if (is_user) { - return 1; + *fsr = 1; + return true; } *prot = PAGE_READ; break; @@ -5794,10 +5825,11 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, break; default: /* Bad permission. */ - return 1; + *fsr = 1; + return true; } *prot |= PAGE_EXEC; - return 0; + return false; } /* get_phys_addr - get the physical address for this virtual address @@ -5806,8 +5838,8 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, * by doing a translation table walk on MMU based systems or using the * MPU state on MPU based systems. * - * Returns 0 if the translation was successful. Otherwise, phys_ptr, attrs, - * prot and page_size may not be filled in, and the return value provides + * Returns false if the translation was successful. Otherwise, phys_ptr, attrs, + * prot and page_size may not be filled in, and the populated fsr value provides * information on why the translation aborted, in the format of a * DFSR/IFSR fault register, with the following caveats: * * we honour the short vs long DFSR format differences. @@ -5823,11 +5855,12 @@ static int get_phys_addr_mpu(CPUARMState *env, uint32_t address, * @attrs: set to the memory transaction attributes to use * @prot: set to the permissions for the page containing phys_ptr * @page_size: set to the size of the page containing phys_ptr + * @fsr: set to the DFSR/IFSR value on failure */ -static inline int get_phys_addr(CPUARMState *env, target_ulong address, - int access_type, ARMMMUIdx mmu_idx, - hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, - target_ulong *page_size) +static inline bool get_phys_addr(CPUARMState *env, target_ulong address, + int access_type, ARMMMUIdx mmu_idx, + hwaddr *phys_ptr, MemTxAttrs *attrs, int *prot, + target_ulong *page_size, uint32_t *fsr) { if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { /* TODO: when we support EL2 we should here call ourselves recursively @@ -5869,28 +5902,28 @@ static inline int get_phys_addr(CPUARMState *env, target_ulong address, if (arm_feature(env, ARM_FEATURE_MPU)) { *page_size = TARGET_PAGE_SIZE; - return get_phys_addr_mpu(env, address, access_type, mmu_idx, phys_ptr, - prot); + return get_phys_addr_pmsav5(env, address, access_type, mmu_idx, + phys_ptr, prot, fsr); } if (regime_using_lpae_format(env, mmu_idx)) { return get_phys_addr_lpae(env, address, access_type, mmu_idx, phys_ptr, - attrs, prot, page_size); + attrs, prot, page_size, fsr); } else if (regime_sctlr(env, mmu_idx) & SCTLR_XP) { return get_phys_addr_v6(env, address, access_type, mmu_idx, phys_ptr, - attrs, prot, page_size); + attrs, prot, page_size, fsr); } else { return get_phys_addr_v5(env, address, access_type, mmu_idx, phys_ptr, - prot, page_size); + prot, page_size, fsr); } } /* Walk the page table and (if the mapping exists) add the page - * to the TLB. Return 0 on success, or an ARM DFSR/IFSR fault - * register format value on failure. + * to the TLB. Return false on success, or true on failure. Populate + * fsr with ARM DFSR/IFSR fault register format value on failure. */ -int arm_tlb_fill(CPUState *cs, vaddr address, - int access_type, int mmu_idx) +bool arm_tlb_fill(CPUState *cs, vaddr address, + int access_type, int mmu_idx, uint32_t *fsr) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -5901,8 +5934,8 @@ int arm_tlb_fill(CPUState *cs, vaddr address, MemTxAttrs attrs = {}; ret = get_phys_addr(env, address, access_type, mmu_idx, &phys_addr, - &attrs, &prot, &page_size); - if (ret == 0) { + &attrs, &prot, &page_size, fsr); + if (!ret) { /* Map a single [sub]page. */ phys_addr &= TARGET_PAGE_MASK; address &= TARGET_PAGE_MASK; @@ -5921,13 +5954,14 @@ hwaddr arm_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) hwaddr phys_addr; target_ulong page_size; int prot; - int ret; + bool ret; + uint32_t fsr; MemTxAttrs attrs = {}; ret = get_phys_addr(env, addr, 0, cpu_mmu_index(env), &phys_addr, - &attrs, &prot, &page_size); + &attrs, &prot, &page_size, &fsr); - if (ret != 0) { + if (ret) { return -1; } diff --git a/target-arm/internals.h b/target-arm/internals.h index 1e5071e..924aff9 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -388,6 +388,7 @@ void arm_handle_psci_call(ARMCPU *cpu); #endif /* Do a page table walk and add page to TLB if possible */ -int arm_tlb_fill(CPUState *cpu, vaddr address, int rw, int mmu_idx); +bool arm_tlb_fill(CPUState *cpu, vaddr address, int rw, int mmu_idx, + uint32_t *fsr); #endif diff --git a/target-arm/kvm-consts.h b/target-arm/kvm-consts.h index aea12f1..943bf89 100644 --- a/target-arm/kvm-consts.h +++ b/target-arm/kvm-consts.h @@ -127,6 +127,8 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED) #define QEMU_KVM_ARM_TARGET_AEM_V8 0 #define QEMU_KVM_ARM_TARGET_FOUNDATION_V8 1 #define QEMU_KVM_ARM_TARGET_CORTEX_A57 2 +#define QEMU_KVM_ARM_TARGET_XGENE_POTENZA 3 +#define QEMU_KVM_ARM_TARGET_CORTEX_A53 4 /* There's no kernel define for this: sentinel value which * matches no KVM target value for either 64 or 32 bit @@ -137,6 +139,8 @@ MISMATCH_CHECK(QEMU_PSCI_RET_DISABLED, PSCI_RET_DISABLED) MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_AEM_V8, KVM_ARM_TARGET_AEM_V8) MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_FOUNDATION_V8, KVM_ARM_TARGET_FOUNDATION_V8) MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A57, KVM_ARM_TARGET_CORTEX_A57) +MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_XGENE_POTENZA, KVM_ARM_TARGET_XGENE_POTENZA) +MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A53, KVM_ARM_TARGET_CORTEX_A53) #else MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A15, KVM_ARM_TARGET_CORTEX_A15) MISMATCH_CHECK(QEMU_KVM_ARM_TARGET_CORTEX_A7, KVM_ARM_TARGET_CORTEX_A7) diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c index 49b6bab..d7e7d68 100644 --- a/target-arm/kvm32.c +++ b/target-arm/kvm32.c @@ -153,10 +153,14 @@ bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx) } } +#define ARM_MPIDR_HWID_BITMASK 0xFFFFFF +#define ARM_CPU_ID_MPIDR 0, 0, 0, 5 + int kvm_arch_init_vcpu(CPUState *cs) { int ret; uint64_t v; + uint32_t mpidr; struct kvm_one_reg r; ARMCPU *cpu = ARM_CPU(cs); @@ -193,6 +197,17 @@ int kvm_arch_init_vcpu(CPUState *cs) return -EINVAL; } + /* + * When KVM is in use, PSCI is emulated in-kernel and not by qemu. + * Currently KVM has its own idea about MPIDR assignment, so we + * override our defaults with what we get from KVM. + */ + ret = kvm_get_one_reg(cs, ARM_CP15_REG32(ARM_CPU_ID_MPIDR), &mpidr); + if (ret) { + return ret; + } + cpu->mp_affinity = mpidr & ARM_MPIDR_HWID_BITMASK; + return kvm_arm_init_cpreg_list(cpu); } diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c index 93c1ca8..ac34f51 100644 --- a/target-arm/kvm64.c +++ b/target-arm/kvm64.c @@ -77,9 +77,13 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUClass *ahcc) return true; } +#define ARM_MPIDR_HWID_BITMASK 0xFF00FFFFFFULL +#define ARM_CPU_ID_MPIDR 3, 0, 0, 0, 5 + int kvm_arch_init_vcpu(CPUState *cs) { int ret; + uint64_t mpidr; ARMCPU *cpu = ARM_CPU(cs); if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE || @@ -107,6 +111,17 @@ int kvm_arch_init_vcpu(CPUState *cs) return ret; } + /* + * When KVM is in use, PSCI is emulated in-kernel and not by qemu. + * Currently KVM has its own idea about MPIDR assignment, so we + * override our defaults with what we get from KVM. + */ + ret = kvm_get_one_reg(cs, ARM64_SYS_REG(ARM_CPU_ID_MPIDR), &mpidr); + if (ret) { + return ret; + } + cpu->mp_affinity = mpidr & ARM_MPIDR_HWID_BITMASK; + return kvm_arm_init_cpreg_list(cpu); } diff --git a/target-arm/machine.c b/target-arm/machine.c index 9446e5a..36365a5 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -40,6 +40,7 @@ static const VMStateDescription vmstate_vfp = { .name = "cpu/vfp", .version_id = 3, .minimum_version_id = 3, + .needed = vfp_needed, .fields = (VMStateField[]) { VMSTATE_FLOAT64_ARRAY(env.vfp.regs, ARMCPU, 64), /* The xregs array is a little awkward because element 1 (FPSCR) @@ -72,6 +73,7 @@ static const VMStateDescription vmstate_iwmmxt = { .name = "cpu/iwmmxt", .version_id = 1, .minimum_version_id = 1, + .needed = iwmmxt_needed, .fields = (VMStateField[]) { VMSTATE_UINT64_ARRAY(env.iwmmxt.regs, ARMCPU, 16), VMSTATE_UINT32_ARRAY(env.iwmmxt.cregs, ARMCPU, 16), @@ -91,6 +93,7 @@ static const VMStateDescription vmstate_m = { .name = "cpu/m", .version_id = 1, .minimum_version_id = 1, + .needed = m_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.v7m.other_sp, ARMCPU), VMSTATE_UINT32(env.v7m.vecbase, ARMCPU), @@ -114,6 +117,7 @@ static const VMStateDescription vmstate_thumb2ee = { .name = "cpu/thumb2ee", .version_id = 1, .minimum_version_id = 1, + .needed = thumb2ee_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.teecr, ARMCPU), VMSTATE_UINT32(env.teehbr, ARMCPU), @@ -282,21 +286,11 @@ const VMStateDescription vmstate_arm_cpu = { VMSTATE_BOOL(powered_off, ARMCPU), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_vfp, - .needed = vfp_needed, - } , { - .vmsd = &vmstate_iwmmxt, - .needed = iwmmxt_needed, - } , { - .vmsd = &vmstate_m, - .needed = m_needed, - } , { - .vmsd = &vmstate_thumb2ee, - .needed = thumb2ee_needed, - } , { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_vfp, + &vmstate_iwmmxt, + &vmstate_m, + &vmstate_thumb2ee, + NULL } }; diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 7583ae7..7fa32c4 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -81,9 +81,10 @@ uint32_t HELPER(neon_tbl)(CPUARMState *env, uint32_t ireg, uint32_t def, void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, uintptr_t retaddr) { - int ret; + bool ret; + uint32_t fsr = 0; - ret = arm_tlb_fill(cs, addr, is_write, mmu_idx); + ret = arm_tlb_fill(cs, addr, is_write, mmu_idx, &fsr); if (unlikely(ret)) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; @@ -96,7 +97,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, } /* AArch64 syndrome does not have an LPAE bit */ - syn = ret & ~(1 << 9); + syn = fsr & ~(1 << 9); /* For insn and data aborts we assume there is no instruction syndrome * information; this is always true for exceptions reported to EL1. @@ -107,13 +108,13 @@ void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, } else { syn = syn_data_abort(same_el, 0, 0, 0, is_write == 1, syn); if (is_write == 1 && arm_feature(env, ARM_FEATURE_V6)) { - ret |= (1 << 11); + fsr |= (1 << 11); } exc = EXCP_DATA_ABORT; } env->exception.vaddress = addr; - env->exception.fsr = ret; + env->exception.fsr = fsr; raise_exception(env, exc, syn, exception_target_el(env)); } } diff --git a/target-arm/psci.c b/target-arm/psci.c index d8fafab..20e4cb6 100644 --- a/target-arm/psci.c +++ b/target-arm/psci.c @@ -72,6 +72,21 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type) } } +static CPUState *get_cpu_by_id(uint64_t id) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + ARMCPU *armcpu = ARM_CPU(cpu); + + if (armcpu->mp_affinity == id) { + return cpu; + } + } + + return NULL; +} + void arm_handle_psci_call(ARMCPU *cpu) { /* @@ -121,7 +136,7 @@ void arm_handle_psci_call(ARMCPU *cpu) switch (param[2]) { case 0: - target_cpu_state = qemu_get_cpu(mpidr & 0xff); + target_cpu_state = get_cpu_by_id(mpidr); if (!target_cpu_state) { ret = QEMU_PSCI_RET_INVALID_PARAMS; break; @@ -153,7 +168,7 @@ void arm_handle_psci_call(ARMCPU *cpu) context_id = param[3]; /* change to the cpu we are powering up */ - target_cpu_state = qemu_get_cpu(mpidr & 0xff); + target_cpu_state = get_cpu_by_id(mpidr); if (!target_cpu_state) { ret = QEMU_PSCI_RET_INVALID_PARAMS; break; diff --git a/target-arm/translate.c b/target-arm/translate.c index 39692d7..ead08f4 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -7175,7 +7175,7 @@ static int disas_coproc_insn(DisasContext *s, uint32_t insn) break; } - gen_set_pc_im(s, s->pc); + gen_set_pc_im(s, s->pc - 4); tmpptr = tcg_const_ptr(ri); tcg_syn = tcg_const_i32(syndrome); gen_helper_access_check_cp_reg(cpu_env, tmpptr, tcg_syn); @@ -9444,6 +9444,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw op = (insn >> 21) & 0xf; if (op == 6) { + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + goto illegal_op; + } /* Halfword pack. */ tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); @@ -9508,6 +9511,27 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw store_reg_bx(s, rd, tmp); break; case 1: /* Sign/zero extend. */ + op = (insn >> 20) & 7; + switch (op) { + case 0: /* SXTAH, SXTH */ + case 1: /* UXTAH, UXTH */ + case 4: /* SXTAB, SXTB */ + case 5: /* UXTAB, UXTB */ + break; + case 2: /* SXTAB16, SXTB16 */ + case 3: /* UXTAB16, UXTB16 */ + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + goto illegal_op; + } + break; + default: + goto illegal_op; + } + if (rn != 15) { + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + goto illegal_op; + } + } tmp = load_reg(s, rm); shift = (insn >> 4) & 3; /* ??? In many cases it's not necessary to do a @@ -9522,7 +9546,8 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw case 3: gen_uxtb16(tmp); break; case 4: gen_sxtb(tmp); break; case 5: gen_uxtb(tmp); break; - default: goto illegal_op; + default: + g_assert_not_reached(); } if (rn != 15) { tmp2 = load_reg(s, rn); @@ -9536,6 +9561,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw store_reg(s, rd, tmp); break; case 2: /* SIMD add/subtract. */ + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + goto illegal_op; + } op = (insn >> 20) & 7; shift = (insn >> 4) & 7; if ((op & 3) == 3 || (shift & 3) == 3) @@ -9550,6 +9578,9 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw op = ((insn >> 17) & 0x38) | ((insn >> 4) & 7); if (op < 4) { /* Saturating add/subtract. */ + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + goto illegal_op; + } tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); if (op & 1) @@ -9560,6 +9591,31 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw gen_helper_add_saturate(tmp, cpu_env, tmp, tmp2); tcg_temp_free_i32(tmp2); } else { + switch (op) { + case 0x0a: /* rbit */ + case 0x08: /* rev */ + case 0x09: /* rev16 */ + case 0x0b: /* revsh */ + case 0x18: /* clz */ + break; + case 0x10: /* sel */ + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + goto illegal_op; + } + break; + case 0x20: /* crc32/crc32c */ + case 0x21: + case 0x22: + case 0x28: + case 0x29: + case 0x2a: + if (!arm_dc_feature(s, ARM_FEATURE_CRC)) { + goto illegal_op; + } + break; + default: + goto illegal_op; + } tmp = load_reg(s, rn); switch (op) { case 0x0a: /* rbit */ @@ -9596,10 +9652,6 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw uint32_t sz = op & 0x3; uint32_t c = op & 0x8; - if (!arm_dc_feature(s, ARM_FEATURE_CRC)) { - goto illegal_op; - } - tmp2 = load_reg(s, rm); if (sz == 0) { tcg_gen_andi_i32(tmp2, tmp2, 0xff); @@ -9617,12 +9669,26 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw break; } default: - goto illegal_op; + g_assert_not_reached(); } } store_reg(s, rd, tmp); break; case 4: case 5: /* 32-bit multiply. Sum of absolute differences. */ + switch ((insn >> 20) & 7) { + case 0: /* 32 x 32 -> 32 */ + case 7: /* Unsigned sum of absolute differences. */ + break; + case 1: /* 16 x 16 -> 32 */ + case 2: /* Dual multiply add. */ + case 3: /* 32 * 16 -> 32msb */ + case 4: /* Dual multiply subtract. */ + case 5: case 6: /* 32 * 32 -> 32msb (SMMUL, SMMLA, SMMLS) */ + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + goto illegal_op; + } + break; + } op = (insn >> 4) & 0xf; tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); @@ -9735,6 +9801,11 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw store_reg(s, rd, tmp); } else if ((op & 0xe) == 0xc) { /* Dual multiply accumulate long. */ + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + goto illegal_op; + } if (op & 1) gen_swap_half(tmp2); gen_smul_dual(tmp, tmp2); @@ -9758,6 +9829,11 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } else { if (op & 8) { /* smlalxy */ + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp); + goto illegal_op; + } gen_mulxy(tmp, tmp2, op & 2, op & 1); tcg_temp_free_i32(tmp2); tmp64 = tcg_temp_new_i64(); @@ -9770,6 +9846,10 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw } if (op & 4) { /* umaal */ + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + tcg_temp_free_i64(tmp64); + goto illegal_op; + } gen_addq_lo(s, tmp64, rs); gen_addq_lo(s, tmp64, rd); } else if (op & 0x40) { @@ -10034,16 +10114,28 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw tmp2 = tcg_const_i32(imm); if (op & 4) { /* Unsigned. */ - if ((op & 1) && shift == 0) + if ((op & 1) && shift == 0) { + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + goto illegal_op; + } gen_helper_usat16(tmp, cpu_env, tmp, tmp2); - else + } else { gen_helper_usat(tmp, cpu_env, tmp, tmp2); + } } else { /* Signed. */ - if ((op & 1) && shift == 0) + if ((op & 1) && shift == 0) { + if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + goto illegal_op; + } gen_helper_ssat16(tmp, cpu_env, tmp, tmp2); - else + } else { gen_helper_ssat(tmp, cpu_env, tmp, tmp2); + } } tcg_temp_free_i32(tmp2); break; diff --git a/target-i386/machine.c b/target-i386/machine.c index 69d86cb..a0df64b 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -403,6 +403,7 @@ static const VMStateDescription vmstate_steal_time_msr = { .name = "cpu/steal_time_msr", .version_id = 1, .minimum_version_id = 1, + .needed = steal_time_msr_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.steal_time_msr, X86CPU), VMSTATE_END_OF_LIST() @@ -413,6 +414,7 @@ static const VMStateDescription vmstate_async_pf_msr = { .name = "cpu/async_pf_msr", .version_id = 1, .minimum_version_id = 1, + .needed = async_pf_msr_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.async_pf_en_msr, X86CPU), VMSTATE_END_OF_LIST() @@ -423,6 +425,7 @@ static const VMStateDescription vmstate_pv_eoi_msr = { .name = "cpu/async_pv_eoi_msr", .version_id = 1, .minimum_version_id = 1, + .needed = pv_eoi_msr_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.pv_eoi_en_msr, X86CPU), VMSTATE_END_OF_LIST() @@ -441,6 +444,7 @@ static const VMStateDescription vmstate_fpop_ip_dp = { .name = "cpu/fpop_ip_dp", .version_id = 1, .minimum_version_id = 1, + .needed = fpop_ip_dp_needed, .fields = (VMStateField[]) { VMSTATE_UINT16(env.fpop, X86CPU), VMSTATE_UINT64(env.fpip, X86CPU), @@ -461,6 +465,7 @@ static const VMStateDescription vmstate_msr_tsc_adjust = { .name = "cpu/msr_tsc_adjust", .version_id = 1, .minimum_version_id = 1, + .needed = tsc_adjust_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.tsc_adjust, X86CPU), VMSTATE_END_OF_LIST() @@ -479,6 +484,7 @@ static const VMStateDescription vmstate_msr_tscdeadline = { .name = "cpu/msr_tscdeadline", .version_id = 1, .minimum_version_id = 1, + .needed = tscdeadline_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.tsc_deadline, X86CPU), VMSTATE_END_OF_LIST() @@ -505,6 +511,7 @@ static const VMStateDescription vmstate_msr_ia32_misc_enable = { .name = "cpu/msr_ia32_misc_enable", .version_id = 1, .minimum_version_id = 1, + .needed = misc_enable_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.msr_ia32_misc_enable, X86CPU), VMSTATE_END_OF_LIST() @@ -515,6 +522,7 @@ static const VMStateDescription vmstate_msr_ia32_feature_control = { .name = "cpu/msr_ia32_feature_control", .version_id = 1, .minimum_version_id = 1, + .needed = feature_control_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.msr_ia32_feature_control, X86CPU), VMSTATE_END_OF_LIST() @@ -549,6 +557,7 @@ static const VMStateDescription vmstate_msr_architectural_pmu = { .name = "cpu/msr_architectural_pmu", .version_id = 1, .minimum_version_id = 1, + .needed = pmu_enable_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.msr_fixed_ctr_ctrl, X86CPU), VMSTATE_UINT64(env.msr_global_ctrl, X86CPU), @@ -584,6 +593,7 @@ static const VMStateDescription vmstate_mpx = { .name = "cpu/mpx", .version_id = 1, .minimum_version_id = 1, + .needed = mpx_needed, .fields = (VMStateField[]) { VMSTATE_BND_REGS(env.bnd_regs, X86CPU, 4), VMSTATE_UINT64(env.bndcs_regs.cfgu, X86CPU), @@ -605,6 +615,7 @@ static const VMStateDescription vmstate_msr_hypercall_hypercall = { .name = "cpu/msr_hyperv_hypercall", .version_id = 1, .minimum_version_id = 1, + .needed = hyperv_hypercall_enable_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.msr_hv_guest_os_id, X86CPU), VMSTATE_UINT64(env.msr_hv_hypercall, X86CPU), @@ -624,6 +635,7 @@ static const VMStateDescription vmstate_msr_hyperv_vapic = { .name = "cpu/msr_hyperv_vapic", .version_id = 1, .minimum_version_id = 1, + .needed = hyperv_vapic_enable_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.msr_hv_vapic, X86CPU), VMSTATE_END_OF_LIST() @@ -642,6 +654,7 @@ static const VMStateDescription vmstate_msr_hyperv_time = { .name = "cpu/msr_hyperv_time", .version_id = 1, .minimum_version_id = 1, + .needed = hyperv_time_enable_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.msr_hv_tsc, X86CPU), VMSTATE_END_OF_LIST() @@ -683,6 +696,7 @@ static const VMStateDescription vmstate_avx512 = { .name = "cpu/avx512", .version_id = 1, .minimum_version_id = 1, + .needed = avx512_needed, .fields = (VMStateField[]) { VMSTATE_UINT64_ARRAY(env.opmask_regs, X86CPU, NB_OPMASK_REGS), VMSTATE_ZMMH_REGS_VARS(env.xmm_regs, X86CPU, 0), @@ -705,6 +719,7 @@ static const VMStateDescription vmstate_xss = { .name = "cpu/xss", .version_id = 1, .minimum_version_id = 1, + .needed = xss_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.xss, X86CPU), VMSTATE_END_OF_LIST() @@ -813,54 +828,22 @@ VMStateDescription vmstate_x86_cpu = { VMSTATE_END_OF_LIST() /* The above list is not sorted /wrt version numbers, watch out! */ }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_async_pf_msr, - .needed = async_pf_msr_needed, - } , { - .vmsd = &vmstate_pv_eoi_msr, - .needed = pv_eoi_msr_needed, - } , { - .vmsd = &vmstate_steal_time_msr, - .needed = steal_time_msr_needed, - } , { - .vmsd = &vmstate_fpop_ip_dp, - .needed = fpop_ip_dp_needed, - }, { - .vmsd = &vmstate_msr_tsc_adjust, - .needed = tsc_adjust_needed, - }, { - .vmsd = &vmstate_msr_tscdeadline, - .needed = tscdeadline_needed, - }, { - .vmsd = &vmstate_msr_ia32_misc_enable, - .needed = misc_enable_needed, - }, { - .vmsd = &vmstate_msr_ia32_feature_control, - .needed = feature_control_needed, - }, { - .vmsd = &vmstate_msr_architectural_pmu, - .needed = pmu_enable_needed, - } , { - .vmsd = &vmstate_mpx, - .needed = mpx_needed, - }, { - .vmsd = &vmstate_msr_hypercall_hypercall, - .needed = hyperv_hypercall_enable_needed, - }, { - .vmsd = &vmstate_msr_hyperv_vapic, - .needed = hyperv_vapic_enable_needed, - }, { - .vmsd = &vmstate_msr_hyperv_time, - .needed = hyperv_time_enable_needed, - }, { - .vmsd = &vmstate_avx512, - .needed = avx512_needed, - }, { - .vmsd = &vmstate_xss, - .needed = xss_needed, - } , { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_async_pf_msr, + &vmstate_pv_eoi_msr, + &vmstate_steal_time_msr, + &vmstate_fpop_ip_dp, + &vmstate_msr_tsc_adjust, + &vmstate_msr_tscdeadline, + &vmstate_msr_ia32_misc_enable, + &vmstate_msr_ia32_feature_control, + &vmstate_msr_architectural_pmu, + &vmstate_mpx, + &vmstate_msr_hypercall_hypercall, + &vmstate_msr_hyperv_vapic, + &vmstate_msr_hyperv_time, + &vmstate_avx512, + &vmstate_xss, + NULL } }; diff --git a/target-mips/cpu.h b/target-mips/cpu.h index f9d2b4c..474a0e3 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -34,7 +34,7 @@ struct r4k_tlb_t { uint_fast16_t RI0:1; uint_fast16_t RI1:1; uint_fast16_t EHINV:1; - target_ulong PFN[2]; + uint64_t PFN[2]; }; #if !defined(CONFIG_USER_ONLY) @@ -100,6 +100,7 @@ struct CPUMIPSFPUContext { float_status fp_status; /* fpu implementation/revision register (fir) */ uint32_t fcr0; +#define FCR0_FREP 29 #define FCR0_UFRP 28 #define FCR0_F64 22 #define FCR0_L 21 @@ -223,8 +224,14 @@ struct CPUMIPSState { uint32_t SEGBITS; uint32_t PABITS; +#if defined(TARGET_MIPS64) +# define PABITS_BASE 36 +#else +# define PABITS_BASE 32 +#endif target_ulong SEGMask; - target_ulong PAMask; + uint64_t PAMask; +#define PAMASK_BASE ((1ULL << PABITS_BASE) - 1) int32_t msair; #define MSAIR_ProcID 8 @@ -272,8 +279,8 @@ struct CPUMIPSState { #define CP0VPEOpt_DWX2 2 #define CP0VPEOpt_DWX1 1 #define CP0VPEOpt_DWX0 0 - target_ulong CP0_EntryLo0; - target_ulong CP0_EntryLo1; + uint64_t CP0_EntryLo0; + uint64_t CP0_EntryLo1; #if defined(TARGET_MIPS64) # define CP0EnLo_RI 63 # define CP0EnLo_XI 62 @@ -288,6 +295,7 @@ struct CPUMIPSState { int32_t CP0_PageGrain; #define CP0PG_RIE 31 #define CP0PG_XIE 30 +#define CP0PG_ELPA 29 #define CP0PG_IEC 27 int32_t CP0_Wired; int32_t CP0_SRSConf0_rw_bitmask; @@ -462,17 +470,21 @@ struct CPUMIPSState { #define CP0C5_CV 29 #define CP0C5_EVA 28 #define CP0C5_MSAEn 27 +#define CP0C5_UFE 9 +#define CP0C5_FRE 8 #define CP0C5_SBRI 6 +#define CP0C5_MVH 5 +#define CP0C5_LLB 4 #define CP0C5_UFR 2 #define CP0C5_NFExists 0 int32_t CP0_Config6; int32_t CP0_Config7; /* XXX: Maybe make LLAddr per-TC? */ - target_ulong lladdr; + uint64_t lladdr; target_ulong llval; target_ulong llnewval; target_ulong llreg; - target_ulong CP0_LLAddr_rw_bitmask; + uint64_t CP0_LLAddr_rw_bitmask; int CP0_LLAddr_shift; target_ulong CP0_WatchLo[8]; int32_t CP0_WatchHi[8]; @@ -499,7 +511,7 @@ struct CPUMIPSState { #define CP0DB_DSS 0 target_ulong CP0_DEPC; int32_t CP0_Performance0; - int32_t CP0_TagLo; + uint64_t CP0_TagLo; int32_t CP0_DataLo; int32_t CP0_TagHi; int32_t CP0_DataHi; @@ -514,7 +526,7 @@ struct CPUMIPSState { #define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */ uint32_t hflags; /* CPU State */ /* TMASK defines different execution modes */ -#define MIPS_HFLAG_TMASK 0x15807FF +#define MIPS_HFLAG_TMASK 0x75807FF #define MIPS_HFLAG_MODE 0x00007 /* execution modes */ /* The KSU flags must be the lowest bits in hflags. The flag order must be the same as defined for CP0 Status. This allows to use @@ -561,6 +573,8 @@ struct CPUMIPSState { #define MIPS_HFLAG_SBRI 0x400000 /* R6 SDBBP causes RI excpt. in user mode */ #define MIPS_HFLAG_FBNSLOT 0x800000 /* Forbidden slot */ #define MIPS_HFLAG_MSA 0x1000000 +#define MIPS_HFLAG_FRE 0x2000000 /* FRE enabled */ +#define MIPS_HFLAG_ELPA 0x4000000 target_ulong btarget; /* Jump / branch target */ target_ulong bcond; /* Branch condition (if needed) */ @@ -796,6 +810,15 @@ static inline void restore_msa_fp_status(CPUMIPSState *env) set_flush_inputs_to_zero(flush_to_zero, status); } +static inline void restore_pamask(CPUMIPSState *env) +{ + if (env->hflags & MIPS_HFLAG_ELPA) { + env->PAMask = (1ULL << env->PABITS) - 1; + } else { + env->PAMask = PAMASK_BASE; + } +} + static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -843,7 +866,8 @@ static inline void compute_hflags(CPUMIPSState *env) env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU | MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 | - MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA); + MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE | + MIPS_HFLAG_ELPA); if (!(env->CP0_Status & (1 << CP0St_EXL)) && !(env->CP0_Status & (1 << CP0St_ERL)) && !(env->hflags & MIPS_HFLAG_DM)) { @@ -924,6 +948,16 @@ static inline void compute_hflags(CPUMIPSState *env) env->hflags |= MIPS_HFLAG_MSA; } } + if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { + if (env->CP0_Config5 & (1 << CP0C5_FRE)) { + env->hflags |= MIPS_HFLAG_FRE; + } + } + if (env->CP0_Config3 & (1 << CP0C3_LPA)) { + if (env->CP0_PageGrain & (1 << CP0PG_ELPA)) { + env->hflags |= MIPS_HFLAG_ELPA; + } + } } #ifndef CONFIG_USER_ONLY diff --git a/target-mips/helper.h b/target-mips/helper.h index 3bd0b02..8df98c7 100644 --- a/target-mips/helper.h +++ b/target-mips/helper.h @@ -348,6 +348,7 @@ DEF_HELPER_1(tlbinvf, void, env) DEF_HELPER_1(di, tl, env) DEF_HELPER_1(ei, tl, env) DEF_HELPER_1(eret, void, env) +DEF_HELPER_1(eretnc, void, env) DEF_HELPER_1(deret, void, env) #endif /* !CONFIG_USER_ONLY */ DEF_HELPER_1(rdhwr_cpunum, tl, env) @@ -931,5 +932,11 @@ DEF_HELPER_4(msa_ftint_u_df, void, env, i32, i32, i32) DEF_HELPER_4(msa_ffint_s_df, void, env, i32, i32, i32) DEF_HELPER_4(msa_ffint_u_df, void, env, i32, i32, i32) -DEF_HELPER_5(msa_ld_df, void, env, i32, i32, i32, s32) -DEF_HELPER_5(msa_st_df, void, env, i32, i32, i32, s32) +#define MSALDST_PROTO(type) \ +DEF_HELPER_3(msa_ld_ ## type, void, env, i32, tl) \ +DEF_HELPER_3(msa_st_ ## type, void, env, i32, tl) +MSALDST_PROTO(b) +MSALDST_PROTO(h) +MSALDST_PROTO(w) +MSALDST_PROTO(d) +#undef MSALDST_PROTO diff --git a/target-mips/machine.c b/target-mips/machine.c index 7d1fa32..8fa755c 100644 --- a/target-mips/machine.c +++ b/target-mips/machine.c @@ -10,6 +10,7 @@ static int cpu_post_load(void *opaque, int version_id) restore_fp_status(env); restore_msa_fp_status(env); compute_hflags(env); + restore_pamask(env); return 0; } @@ -142,8 +143,8 @@ static int get_tlb(QEMUFile *f, void *pv, size_t size) v->RI0 = (flags >> 13) & 1; v->XI1 = (flags >> 12) & 1; v->XI0 = (flags >> 11) & 1; - qemu_get_betls(f, &v->PFN[0]); - qemu_get_betls(f, &v->PFN[1]); + qemu_get_be64s(f, &v->PFN[0]); + qemu_get_be64s(f, &v->PFN[1]); return 0; } @@ -169,8 +170,8 @@ static void put_tlb(QEMUFile *f, void *pv, size_t size) qemu_put_be32s(f, &v->PageMask); qemu_put_8s(f, &v->ASID); qemu_put_be16s(f, &flags); - qemu_put_betls(f, &v->PFN[0]); - qemu_put_betls(f, &v->PFN[1]); + qemu_put_be64s(f, &v->PFN[0]); + qemu_put_be64s(f, &v->PFN[1]); } const VMStateInfo vmstate_info_tlb = { @@ -201,8 +202,8 @@ const VMStateDescription vmstate_tlb = { const VMStateDescription vmstate_mips_cpu = { .name = "cpu", - .version_id = 6, - .minimum_version_id = 6, + .version_id = 7, + .minimum_version_id = 7, .post_load = cpu_post_load, .fields = (VMStateField[]) { /* Active TC */ @@ -237,8 +238,8 @@ const VMStateDescription vmstate_mips_cpu = { VMSTATE_UINTTL(env.CP0_VPESchedule, MIPSCPU), VMSTATE_UINTTL(env.CP0_VPEScheFBack, MIPSCPU), VMSTATE_INT32(env.CP0_VPEOpt, MIPSCPU), - VMSTATE_UINTTL(env.CP0_EntryLo0, MIPSCPU), - VMSTATE_UINTTL(env.CP0_EntryLo1, MIPSCPU), + VMSTATE_UINT64(env.CP0_EntryLo0, MIPSCPU), + VMSTATE_UINT64(env.CP0_EntryLo1, MIPSCPU), VMSTATE_UINTTL(env.CP0_Context, MIPSCPU), VMSTATE_INT32(env.CP0_PageMask, MIPSCPU), VMSTATE_INT32(env.CP0_PageGrain, MIPSCPU), @@ -269,7 +270,7 @@ const VMStateDescription vmstate_mips_cpu = { VMSTATE_INT32(env.CP0_Config3, MIPSCPU), VMSTATE_INT32(env.CP0_Config6, MIPSCPU), VMSTATE_INT32(env.CP0_Config7, MIPSCPU), - VMSTATE_UINTTL(env.lladdr, MIPSCPU), + VMSTATE_UINT64(env.lladdr, MIPSCPU), VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8), VMSTATE_INT32_ARRAY(env.CP0_WatchHi, MIPSCPU, 8), VMSTATE_UINTTL(env.CP0_XContext, MIPSCPU), @@ -277,7 +278,7 @@ const VMStateDescription vmstate_mips_cpu = { VMSTATE_INT32(env.CP0_Debug, MIPSCPU), VMSTATE_UINTTL(env.CP0_DEPC, MIPSCPU), VMSTATE_INT32(env.CP0_Performance0, MIPSCPU), - VMSTATE_INT32(env.CP0_TagLo, MIPSCPU), + VMSTATE_UINT64(env.CP0_TagLo, MIPSCPU), VMSTATE_INT32(env.CP0_DataLo, MIPSCPU), VMSTATE_INT32(env.CP0_TagHi, MIPSCPU), VMSTATE_INT32(env.CP0_DataHi, MIPSCPU), diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h index 1784227..20aa87c 100644 --- a/target-mips/mips-defs.h +++ b/target-mips/mips-defs.h @@ -10,11 +10,11 @@ #if defined(TARGET_MIPS64) #define TARGET_LONG_BITS 64 -#define TARGET_PHYS_ADDR_SPACE_BITS 36 +#define TARGET_PHYS_ADDR_SPACE_BITS 48 #define TARGET_VIRT_ADDR_SPACE_BITS 42 #else #define TARGET_LONG_BITS 32 -#define TARGET_PHYS_ADDR_SPACE_BITS 36 +#define TARGET_PHYS_ADDR_SPACE_BITS 40 #define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 73a8e45..2a9ddff 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -90,10 +90,10 @@ static inline type do_##name(CPUMIPSState *env, target_ulong addr, \ } \ } #endif -HELPER_LD(lbu, ldub, uint8_t) -HELPER_LD(lhu, lduw, uint16_t) HELPER_LD(lw, ldl, int32_t) +#if defined(TARGET_MIPS64) HELPER_LD(ld, ldq, int64_t) +#endif #undef HELPER_LD #if defined(CONFIG_USER_ONLY) @@ -118,9 +118,10 @@ static inline void do_##name(CPUMIPSState *env, target_ulong addr, \ } #endif HELPER_ST(sb, stb, uint8_t) -HELPER_ST(sh, stw, uint16_t) HELPER_ST(sw, stl, uint32_t) +#if defined(TARGET_MIPS64) HELPER_ST(sd, stq, uint64_t) +#endif #undef HELPER_ST target_ulong helper_clo (target_ulong arg1) @@ -1067,19 +1068,23 @@ void helper_mtc0_vpeopt(CPUMIPSState *env, target_ulong arg1) env->CP0_VPEOpt = arg1 & 0x0000ffff; } +#define MTC0_ENTRYLO_MASK(env) ((env->PAMask >> 6) & 0x3FFFFFFF) + void helper_mtc0_entrylo0(CPUMIPSState *env, target_ulong arg1) { - /* Large physaddr (PABITS) not implemented */ /* 1k pages not implemented */ target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE)); - env->CP0_EntryLo0 = (arg1 & 0x3FFFFFFF) | (rxi << (CP0EnLo_XI - 30)); + env->CP0_EntryLo0 = (arg1 & MTC0_ENTRYLO_MASK(env)) + | (rxi << (CP0EnLo_XI - 30)); } #if defined(TARGET_MIPS64) +#define DMTC0_ENTRYLO_MASK(env) (env->PAMask >> 6) + void helper_dmtc0_entrylo0(CPUMIPSState *env, uint64_t arg1) { uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32); - env->CP0_EntryLo0 = (arg1 & 0x3FFFFFFF) | rxi; + env->CP0_EntryLo0 = (arg1 & DMTC0_ENTRYLO_MASK(env)) | rxi; } #endif @@ -1245,17 +1250,17 @@ void helper_mttc0_tcschefback(CPUMIPSState *env, target_ulong arg1) void helper_mtc0_entrylo1(CPUMIPSState *env, target_ulong arg1) { - /* Large physaddr (PABITS) not implemented */ /* 1k pages not implemented */ target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE)); - env->CP0_EntryLo1 = (arg1 & 0x3FFFFFFF) | (rxi << (CP0EnLo_XI - 30)); + env->CP0_EntryLo1 = (arg1 & MTC0_ENTRYLO_MASK(env)) + | (rxi << (CP0EnLo_XI - 30)); } #if defined(TARGET_MIPS64) void helper_dmtc0_entrylo1(CPUMIPSState *env, uint64_t arg1) { uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32); - env->CP0_EntryLo1 = (arg1 & 0x3FFFFFFF) | rxi; + env->CP0_EntryLo1 = (arg1 & DMTC0_ENTRYLO_MASK(env)) | rxi; } #endif @@ -1278,10 +1283,11 @@ void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) { /* SmartMIPS not implemented */ - /* Large physaddr (PABITS) not implemented */ /* 1k pages not implemented */ env->CP0_PageGrain = (arg1 & env->CP0_PageGrain_rw_bitmask) | (env->CP0_PageGrain & ~env->CP0_PageGrain_rw_bitmask); + compute_hflags(env); + restore_pamask(env); } void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1) @@ -1825,6 +1831,16 @@ static void r4k_mips_tlb_flush_extra (CPUMIPSState *env, int first) } } +static inline uint64_t get_tlb_pfn_from_entrylo(uint64_t entrylo) +{ +#if defined(TARGET_MIPS64) + return extract64(entrylo, 6, 54); +#else + return extract64(entrylo, 6, 24) | /* PFN */ + (extract64(entrylo, 32, 32) << 24); /* PFNX */ +#endif +} + static void r4k_fill_tlb(CPUMIPSState *env, int idx) { r4k_tlb_t *tlb; @@ -1848,13 +1864,13 @@ static void r4k_fill_tlb(CPUMIPSState *env, int idx) tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7; tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1; tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1; - tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12; + tlb->PFN[0] = get_tlb_pfn_from_entrylo(env->CP0_EntryLo0) << 12; tlb->V1 = (env->CP0_EntryLo1 & 2) != 0; tlb->D1 = (env->CP0_EntryLo1 & 4) != 0; tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7; tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1; tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1; - tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12; + tlb->PFN[1] = get_tlb_pfn_from_entrylo(env->CP0_EntryLo1) << 12; } void r4k_helper_tlbinv(CPUMIPSState *env) @@ -1971,6 +1987,16 @@ void r4k_helper_tlbp(CPUMIPSState *env) } } +static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn) +{ +#if defined(TARGET_MIPS64) + return tlb_pfn << 6; +#else + return (extract64(tlb_pfn, 0, 24) << 6) | /* PFN */ + (extract64(tlb_pfn, 24, 32) << 32); /* PFNX */ +#endif +} + void r4k_helper_tlbr(CPUMIPSState *env) { r4k_tlb_t *tlb; @@ -1996,13 +2022,13 @@ void r4k_helper_tlbr(CPUMIPSState *env) env->CP0_EntryHi = tlb->VPN | tlb->ASID; env->CP0_PageMask = tlb->PageMask; env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) | - ((target_ulong)tlb->RI0 << CP0EnLo_RI) | - ((target_ulong)tlb->XI0 << CP0EnLo_XI) | - (tlb->C0 << 3) | (tlb->PFN[0] >> 6); + ((uint64_t)tlb->RI0 << CP0EnLo_RI) | + ((uint64_t)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) | + get_entrylo_pfn_from_tlb(tlb->PFN[0] >> 12); env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) | - ((target_ulong)tlb->RI1 << CP0EnLo_RI) | - ((target_ulong)tlb->XI1 << CP0EnLo_XI) | - (tlb->C1 << 3) | (tlb->PFN[1] >> 6); + ((uint64_t)tlb->RI1 << CP0EnLo_RI) | + ((uint64_t)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) | + get_entrylo_pfn_from_tlb(tlb->PFN[1] >> 12); } } @@ -2098,7 +2124,7 @@ static void set_pc(CPUMIPSState *env, target_ulong error_pc) } } -void helper_eret(CPUMIPSState *env) +static inline void exception_return(CPUMIPSState *env) { debug_pre_eret(env); if (env->CP0_Status & (1 << CP0St_ERL)) { @@ -2110,9 +2136,19 @@ void helper_eret(CPUMIPSState *env) } compute_hflags(env); debug_post_eret(env); +} + +void helper_eret(CPUMIPSState *env) +{ + exception_return(env); env->lladdr = 1; } +void helper_eretnc(CPUMIPSState *env) +{ + exception_return(env); +} + void helper_deret(CPUMIPSState *env) { debug_pre_eret(env); @@ -2303,6 +2339,16 @@ target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg) } } break; + case 5: + /* FRE Support - read Config5.FRE bit */ + if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { + if (env->CP0_Config5 & (1 << CP0C5_UFE)) { + arg1 = (env->CP0_Config5 >> CP0C5_FRE) & 1; + } else { + helper_raise_exception(env, EXCP_RI); + } + } + break; case 25: arg1 = ((env->active_fpu.fcr31 >> 24) & 0xfe) | ((env->active_fpu.fcr31 >> 23) & 0x1); break; @@ -2347,6 +2393,30 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) helper_raise_exception(env, EXCP_RI); } break; + case 5: + /* FRE Support - clear Config5.FRE bit */ + if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) { + return; + } + if (env->CP0_Config5 & (1 << CP0C5_UFE)) { + env->CP0_Config5 &= ~(1 << CP0C5_FRE); + compute_hflags(env); + } else { + helper_raise_exception(env, EXCP_RI); + } + break; + case 6: + /* FRE Support - set Config5.FRE bit */ + if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) { + return; + } + if (env->CP0_Config5 & (1 << CP0C5_UFE)) { + env->CP0_Config5 |= (1 << CP0C5_FRE); + compute_hflags(env); + } else { + helper_raise_exception(env, EXCP_RI); + } + break; case 25: if ((env->insn_flags & ISA_MIPS32R6) || (arg1 & 0xffffff00)) { return; @@ -3558,72 +3628,82 @@ FOP_CONDN_S(sne, (float32_lt(fst1, fst0, &env->active_fpu.fp_status) /* Element-by-element access macros */ #define DF_ELEMENTS(df) (MSA_WRLEN / DF_BITS(df)) -void helper_msa_ld_df(CPUMIPSState *env, uint32_t df, uint32_t wd, uint32_t rs, - int32_t s10) -{ - wr_t *pwd = &(env->active_fpu.fpr[wd].wr); - target_ulong addr = env->active_tc.gpr[rs] + (s10 << df); - int i; +#if !defined(CONFIG_USER_ONLY) +#define MEMOP_IDX(DF) \ + TCGMemOpIdx oi = make_memop_idx(MO_TE | DF | MO_UNALN, \ + cpu_mmu_index(env)); +#else +#define MEMOP_IDX(DF) +#endif - switch (df) { - case DF_BYTE: - for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { - pwd->b[i] = do_lbu(env, addr + (i << DF_BYTE), - env->hflags & MIPS_HFLAG_KSU); - } - break; - case DF_HALF: - for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { - pwd->h[i] = do_lhu(env, addr + (i << DF_HALF), - env->hflags & MIPS_HFLAG_KSU); - } - break; - case DF_WORD: - for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { - pwd->w[i] = do_lw(env, addr + (i << DF_WORD), - env->hflags & MIPS_HFLAG_KSU); - } - break; - case DF_DOUBLE: - for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { - pwd->d[i] = do_ld(env, addr + (i << DF_DOUBLE), - env->hflags & MIPS_HFLAG_KSU); - } - break; - } +#define MSA_LD_DF(DF, TYPE, LD_INSN, ...) \ +void helper_msa_ld_ ## TYPE(CPUMIPSState *env, uint32_t wd, \ + target_ulong addr) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + wr_t wx; \ + int i; \ + MEMOP_IDX(DF) \ + for (i = 0; i < DF_ELEMENTS(DF); i++) { \ + wx.TYPE[i] = LD_INSN(env, addr + (i << DF), ##__VA_ARGS__); \ + } \ + memcpy(pwd, &wx, sizeof(wr_t)); \ } -void helper_msa_st_df(CPUMIPSState *env, uint32_t df, uint32_t wd, uint32_t rs, - int32_t s10) -{ - wr_t *pwd = &(env->active_fpu.fpr[wd].wr); - target_ulong addr = env->active_tc.gpr[rs] + (s10 << df); - int i; +#if !defined(CONFIG_USER_ONLY) +MSA_LD_DF(DF_BYTE, b, helper_ret_ldub_mmu, oi, GETRA()) +MSA_LD_DF(DF_HALF, h, helper_ret_lduw_mmu, oi, GETRA()) +MSA_LD_DF(DF_WORD, w, helper_ret_ldul_mmu, oi, GETRA()) +MSA_LD_DF(DF_DOUBLE, d, helper_ret_ldq_mmu, oi, GETRA()) +#else +MSA_LD_DF(DF_BYTE, b, cpu_ldub_data) +MSA_LD_DF(DF_HALF, h, cpu_lduw_data) +MSA_LD_DF(DF_WORD, w, cpu_ldl_data) +MSA_LD_DF(DF_DOUBLE, d, cpu_ldq_data) +#endif - switch (df) { - case DF_BYTE: - for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { - do_sb(env, addr + (i << DF_BYTE), pwd->b[i], - env->hflags & MIPS_HFLAG_KSU); - } - break; - case DF_HALF: - for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { - do_sh(env, addr + (i << DF_HALF), pwd->h[i], - env->hflags & MIPS_HFLAG_KSU); - } - break; - case DF_WORD: - for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { - do_sw(env, addr + (i << DF_WORD), pwd->w[i], - env->hflags & MIPS_HFLAG_KSU); - } - break; - case DF_DOUBLE: - for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { - do_sd(env, addr + (i << DF_DOUBLE), pwd->d[i], - env->hflags & MIPS_HFLAG_KSU); - } - break; +#define MSA_PAGESPAN(x) \ + ((((x) & ~TARGET_PAGE_MASK) + MSA_WRLEN/8 - 1) >= TARGET_PAGE_SIZE) + +static inline void ensure_writable_pages(CPUMIPSState *env, + target_ulong addr, + int mmu_idx, + uintptr_t retaddr) +{ +#if !defined(CONFIG_USER_ONLY) + target_ulong page_addr; + if (unlikely(MSA_PAGESPAN(addr))) { + /* first page */ + probe_write(env, addr, mmu_idx, retaddr); + /* second page */ + page_addr = (addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + probe_write(env, page_addr, mmu_idx, retaddr); } +#endif +} + +#define MSA_ST_DF(DF, TYPE, ST_INSN, ...) \ +void helper_msa_st_ ## TYPE(CPUMIPSState *env, uint32_t wd, \ + target_ulong addr) \ +{ \ + wr_t *pwd = &(env->active_fpu.fpr[wd].wr); \ + int mmu_idx = cpu_mmu_index(env); \ + int i; \ + MEMOP_IDX(DF) \ + ensure_writable_pages(env, addr, mmu_idx, GETRA()); \ + for (i = 0; i < DF_ELEMENTS(DF); i++) { \ + ST_INSN(env, addr + (i << DF), pwd->TYPE[i], ##__VA_ARGS__); \ + } \ } + +#if !defined(CONFIG_USER_ONLY) +MSA_ST_DF(DF_BYTE, b, helper_ret_stb_mmu, oi, GETRA()) +MSA_ST_DF(DF_HALF, h, helper_ret_stw_mmu, oi, GETRA()) +MSA_ST_DF(DF_WORD, w, helper_ret_stl_mmu, oi, GETRA()) +MSA_ST_DF(DF_DOUBLE, d, helper_ret_stq_mmu, oi, GETRA()) +#else +MSA_ST_DF(DF_BYTE, b, cpu_stb_data) +MSA_ST_DF(DF_HALF, h, cpu_stw_data) +MSA_ST_DF(DF_WORD, w, cpu_stl_data) +MSA_ST_DF(DF_DOUBLE, d, cpu_stq_data) +#endif diff --git a/target-mips/translate.c b/target-mips/translate.c index fd063a2..1d128ee 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -868,8 +868,10 @@ enum { enum { OPC_MFC0 = (0x00 << 21) | OPC_CP0, OPC_DMFC0 = (0x01 << 21) | OPC_CP0, + OPC_MFHC0 = (0x02 << 21) | OPC_CP0, OPC_MTC0 = (0x04 << 21) | OPC_CP0, OPC_DMTC0 = (0x05 << 21) | OPC_CP0, + OPC_MTHC0 = (0x06 << 21) | OPC_CP0, OPC_MFTR = (0x08 << 21) | OPC_CP0, OPC_RDPGPR = (0x0A << 21) | OPC_CP0, OPC_MFMC0 = (0x0B << 21) | OPC_CP0, @@ -1414,6 +1416,7 @@ typedef struct DisasContext { int32_t CP0_Config1; /* Routine used to access memory */ int mem_idx; + TCGMemOp default_tcg_memop_mask; uint32_t hflags, saved_hflags; int bstate; target_ulong btarget; @@ -1423,6 +1426,9 @@ typedef struct DisasContext { int ie; bool bi; bool bp; + uint64_t PAMask; + bool mvh; + int CP0_LLAddr_shift; } DisasContext; enum { @@ -1557,15 +1563,80 @@ static inline void gen_store_srsgpr (int from, int to) } } +/* Tests */ +static inline void gen_save_pc(target_ulong pc) +{ + tcg_gen_movi_tl(cpu_PC, pc); +} + +static inline void save_cpu_state(DisasContext *ctx, int do_save_pc) +{ + LOG_DISAS("hflags %08x saved %08x\n", ctx->hflags, ctx->saved_hflags); + if (do_save_pc && ctx->pc != ctx->saved_pc) { + gen_save_pc(ctx->pc); + ctx->saved_pc = ctx->pc; + } + if (ctx->hflags != ctx->saved_hflags) { + tcg_gen_movi_i32(hflags, ctx->hflags); + ctx->saved_hflags = ctx->hflags; + switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) { + case MIPS_HFLAG_BR: + break; + case MIPS_HFLAG_BC: + case MIPS_HFLAG_BL: + case MIPS_HFLAG_B: + tcg_gen_movi_tl(btarget, ctx->btarget); + break; + } + } +} + +static inline void restore_cpu_state(CPUMIPSState *env, DisasContext *ctx) +{ + ctx->saved_hflags = ctx->hflags; + switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) { + case MIPS_HFLAG_BR: + break; + case MIPS_HFLAG_BC: + case MIPS_HFLAG_BL: + case MIPS_HFLAG_B: + ctx->btarget = env->btarget; + break; + } +} + +static inline void generate_exception_err(DisasContext *ctx, int excp, int err) +{ + TCGv_i32 texcp = tcg_const_i32(excp); + TCGv_i32 terr = tcg_const_i32(err); + save_cpu_state(ctx, 1); + gen_helper_raise_exception_err(cpu_env, texcp, terr); + tcg_temp_free_i32(terr); + tcg_temp_free_i32(texcp); +} + +static inline void generate_exception(DisasContext *ctx, int excp) +{ + save_cpu_state(ctx, 1); + gen_helper_0e0i(raise_exception, excp); +} + /* Floating point register moves. */ -static void gen_load_fpr32(TCGv_i32 t, int reg) +static void gen_load_fpr32(DisasContext *ctx, TCGv_i32 t, int reg) { + if (ctx->hflags & MIPS_HFLAG_FRE) { + generate_exception(ctx, EXCP_RI); + } tcg_gen_trunc_i64_i32(t, fpu_f64[reg]); } -static void gen_store_fpr32(TCGv_i32 t, int reg) +static void gen_store_fpr32(DisasContext *ctx, TCGv_i32 t, int reg) { - TCGv_i64 t64 = tcg_temp_new_i64(); + TCGv_i64 t64; + if (ctx->hflags & MIPS_HFLAG_FRE) { + generate_exception(ctx, EXCP_RI); + } + t64 = tcg_temp_new_i64(); tcg_gen_extu_i32_i64(t64, t); tcg_gen_deposit_i64(fpu_f64[reg], fpu_f64[reg], t64, 0, 32); tcg_temp_free_i64(t64); @@ -1579,7 +1650,7 @@ static void gen_load_fpr32h(DisasContext *ctx, TCGv_i32 t, int reg) tcg_gen_trunc_i64_i32(t, t64); tcg_temp_free_i64(t64); } else { - gen_load_fpr32(t, reg | 1); + gen_load_fpr32(ctx, t, reg | 1); } } @@ -1591,7 +1662,7 @@ static void gen_store_fpr32h(DisasContext *ctx, TCGv_i32 t, int reg) tcg_gen_deposit_i64(fpu_f64[reg], fpu_f64[reg], t64, 32, 32); tcg_temp_free_i64(t64); } else { - gen_store_fpr32(t, reg | 1); + gen_store_fpr32(ctx, t, reg | 1); } } @@ -1626,66 +1697,6 @@ static inline int get_fp_bit (int cc) return 23; } -/* Tests */ -static inline void gen_save_pc(target_ulong pc) -{ - tcg_gen_movi_tl(cpu_PC, pc); -} - -static inline void save_cpu_state (DisasContext *ctx, int do_save_pc) -{ - LOG_DISAS("hflags %08x saved %08x\n", ctx->hflags, ctx->saved_hflags); - if (do_save_pc && ctx->pc != ctx->saved_pc) { - gen_save_pc(ctx->pc); - ctx->saved_pc = ctx->pc; - } - if (ctx->hflags != ctx->saved_hflags) { - tcg_gen_movi_i32(hflags, ctx->hflags); - ctx->saved_hflags = ctx->hflags; - switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) { - case MIPS_HFLAG_BR: - break; - case MIPS_HFLAG_BC: - case MIPS_HFLAG_BL: - case MIPS_HFLAG_B: - tcg_gen_movi_tl(btarget, ctx->btarget); - break; - } - } -} - -static inline void restore_cpu_state (CPUMIPSState *env, DisasContext *ctx) -{ - ctx->saved_hflags = ctx->hflags; - switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) { - case MIPS_HFLAG_BR: - break; - case MIPS_HFLAG_BC: - case MIPS_HFLAG_BL: - case MIPS_HFLAG_B: - ctx->btarget = env->btarget; - break; - } -} - -static inline void -generate_exception_err (DisasContext *ctx, int excp, int err) -{ - TCGv_i32 texcp = tcg_const_i32(excp); - TCGv_i32 terr = tcg_const_i32(err); - save_cpu_state(ctx, 1); - gen_helper_raise_exception_err(cpu_env, texcp, terr); - tcg_temp_free_i32(terr); - tcg_temp_free_i32(texcp); -} - -static inline void -generate_exception (DisasContext *ctx, int excp) -{ - save_cpu_state(ctx, 1); - gen_helper_0e0i(raise_exception, excp); -} - /* Addresses computation */ static inline void gen_op_addr_add (DisasContext *ctx, TCGv ret, TCGv arg0, TCGv arg1) { @@ -1815,11 +1826,20 @@ static inline void check_mips_64(DisasContext *ctx) } #endif +#ifndef CONFIG_USER_ONLY +static inline void check_mvh(DisasContext *ctx) +{ + if (unlikely(!ctx->mvh)) { + generate_exception(ctx, EXCP_RI); + } +} +#endif + /* Define small wrappers for gen_load_fpr* so that we have a uniform calling interface for 32 and 64-bit FPRs. No sense in changing all callers for gen_load_fpr32 when we need the CTX parameter for this one use. */ -#define gen_ldcmp_fpr32(ctx, x, y) gen_load_fpr32(x, y) +#define gen_ldcmp_fpr32(ctx, x, y) gen_load_fpr32(ctx, x, y) #define gen_ldcmp_fpr64(ctx, x, y) gen_load_fpr64(ctx, x, y) #define FOP_CONDS(type, abs, fmt, ifmt, bits) \ static inline void gen_cmp ## type ## _ ## fmt(DisasContext *ctx, int n, \ @@ -1963,7 +1983,7 @@ static inline void gen_r6_cmp_ ## fmt(DisasContext * ctx, int n, \ } FOP_CONDNS(d, FMT_D, 64, gen_store_fpr64(ctx, fp0, fd)) -FOP_CONDNS(s, FMT_S, 32, gen_store_fpr32(fp0, fd)) +FOP_CONDNS(s, FMT_S, 32, gen_store_fpr32(ctx, fp0, fd)) #undef FOP_CONDNS #undef gen_ldcmp_fpr32 #undef gen_ldcmp_fpr64 @@ -2081,12 +2101,14 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, switch (opc) { #if defined(TARGET_MIPS64) case OPC_LWU: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL | + ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); opn = "lwu"; break; case OPC_LD: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ | + ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); opn = "ld"; break; @@ -2157,17 +2179,20 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, opn = "lwpc"; break; case OPC_LW: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL | + ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); opn = "lw"; break; case OPC_LH: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW | + ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); opn = "lh"; break; case OPC_LHU: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUW); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUW | + ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); opn = "lhu"; break; @@ -2251,7 +2276,8 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt, switch (opc) { #if defined(TARGET_MIPS64) case OPC_SD: - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ | + ctx->default_tcg_memop_mask); opn = "sd"; break; case OPC_SDL: @@ -2266,11 +2292,13 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt, break; #endif case OPC_SW: - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | + ctx->default_tcg_memop_mask); opn = "sw"; break; case OPC_SH: - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW | + ctx->default_tcg_memop_mask); opn = "sh"; break; case OPC_SB: @@ -2347,8 +2375,9 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, case OPC_LWC1: { TCGv_i32 fp0 = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL); - gen_store_fpr32(fp0, ft); + tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL | + ctx->default_tcg_memop_mask); + gen_store_fpr32(ctx, fp0, ft); tcg_temp_free_i32(fp0); } opn = "lwc1"; @@ -2356,8 +2385,9 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, case OPC_SWC1: { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, ft); - tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL); + gen_load_fpr32(ctx, fp0, ft); + tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL | + ctx->default_tcg_memop_mask); tcg_temp_free_i32(fp0); } opn = "swc1"; @@ -2365,7 +2395,8 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, case OPC_LDC1: { TCGv_i64 fp0 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEQ); + tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEQ | + ctx->default_tcg_memop_mask); gen_store_fpr64(ctx, fp0, ft); tcg_temp_free_i64(fp0); } @@ -2375,7 +2406,8 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, { TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, ft); - tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ); + tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ | + ctx->default_tcg_memop_mask); tcg_temp_free_i64(fp0); } opn = "sdc1"; @@ -4815,6 +4847,69 @@ static void gen_bshfl (DisasContext *ctx, uint32_t op2, int rt, int rd) #ifndef CONFIG_USER_ONLY /* CP0 (MMU and control) */ +static inline void gen_move_low32(TCGv ret, TCGv_i64 arg) +{ +#if defined(TARGET_MIPS64) + tcg_gen_ext32s_tl(ret, arg); +#else + tcg_gen_trunc_i64_tl(ret, arg); +#endif +} + +static inline void gen_mthc0_entrylo(TCGv arg, target_ulong off) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_ext_tl_i64(t0, arg); + tcg_gen_ld_i64(t1, cpu_env, off); +#if defined(TARGET_MIPS64) + tcg_gen_deposit_i64(t1, t1, t0, 30, 32); +#else + tcg_gen_concat32_i64(t1, t1, t0); +#endif + tcg_gen_st_i64(t1, cpu_env, off); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t0); +} + +static inline void gen_mthc0_store64(TCGv arg, target_ulong off) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_ext_tl_i64(t0, arg); + tcg_gen_ld_i64(t1, cpu_env, off); + tcg_gen_concat32_i64(t1, t1, t0); + tcg_gen_st_i64(t1, cpu_env, off); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t0); +} + +static inline void gen_mfhc0_entrylo(TCGv arg, target_ulong off) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + + tcg_gen_ld_i64(t0, cpu_env, off); +#if defined(TARGET_MIPS64) + tcg_gen_shri_i64(t0, t0, 30); +#else + tcg_gen_shri_i64(t0, t0, 32); +#endif + gen_move_low32(arg, t0); + tcg_temp_free_i64(t0); +} + +static inline void gen_mfhc0_load64(TCGv arg, target_ulong off, int shift) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + + tcg_gen_ld_i64(t0, cpu_env, off); + tcg_gen_shri_i64(t0, t0, 32 + shift); + gen_move_low32(arg, t0); + tcg_temp_free_i64(t0); +} + static inline void gen_mfc0_load32 (TCGv arg, target_ulong off) { TCGv_i32 t0 = tcg_temp_new_i32(); @@ -4845,6 +4940,140 @@ static inline void gen_mtc0_store64 (TCGv arg, target_ulong off) tcg_gen_st_tl(arg, cpu_env, off); } +static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) +{ + const char *rn = "invalid"; + + if (!(ctx->hflags & MIPS_HFLAG_ELPA)) { + goto mfhc0_read_zero; + } + + switch (reg) { + case 2: + switch (sel) { + case 0: + gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0)); + rn = "EntryLo0"; + break; + default: + goto mfhc0_read_zero; + } + break; + case 3: + switch (sel) { + case 0: + gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1)); + rn = "EntryLo1"; + break; + default: + goto mfhc0_read_zero; + } + break; + case 17: + switch (sel) { + case 0: + gen_mfhc0_load64(arg, offsetof(CPUMIPSState, lladdr), + ctx->CP0_LLAddr_shift); + rn = "LLAddr"; + break; + default: + goto mfhc0_read_zero; + } + break; + case 28: + switch (sel) { + case 0: + case 2: + case 4: + case 6: + gen_mfhc0_load64(arg, offsetof(CPUMIPSState, CP0_TagLo), 0); + rn = "TagLo"; + break; + default: + goto mfhc0_read_zero; + } + break; + default: + goto mfhc0_read_zero; + } + + (void)rn; /* avoid a compiler warning */ + LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); + return; + +mfhc0_read_zero: + LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); + tcg_gen_movi_tl(arg, 0); +} + +static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) +{ + const char *rn = "invalid"; + uint64_t mask = ctx->PAMask >> 36; + + if (!(ctx->hflags & MIPS_HFLAG_ELPA)) { + goto mthc0_nop; + } + + switch (reg) { + case 2: + switch (sel) { + case 0: + tcg_gen_andi_tl(arg, arg, mask); + gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0)); + rn = "EntryLo0"; + break; + default: + goto mthc0_nop; + } + break; + case 3: + switch (sel) { + case 0: + tcg_gen_andi_tl(arg, arg, mask); + gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1)); + rn = "EntryLo1"; + break; + default: + goto mthc0_nop; + } + break; + case 17: + switch (sel) { + case 0: + /* LLAddr is read-only (the only exception is bit 0 if LLB is + supported); the CP0_LLAddr_rw_bitmask does not seem to be + relevant for modern MIPS cores supporting MTHC0, therefore + treating MTHC0 to LLAddr as NOP. */ + rn = "LLAddr"; + break; + default: + goto mthc0_nop; + } + break; + case 28: + switch (sel) { + case 0: + case 2: + case 4: + case 6: + tcg_gen_andi_tl(arg, arg, mask); + gen_mthc0_store64(arg, offsetof(CPUMIPSState, CP0_TagLo)); + rn = "TagLo"; + break; + default: + goto mthc0_nop; + } + break; + default: + goto mthc0_nop; + } + + (void)rn; /* avoid a compiler warning */ +mthc0_nop: + LOG_DISAS("mthc0 %s (reg %d sel %d)\n", rn, reg, sel); +} + static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg) { if (ctx->insn_flags & ISA_MIPS32R6) { @@ -4943,17 +5172,20 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 2: switch (sel) { case 0: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo0)); + { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp, cpu_env, + offsetof(CPUMIPSState, CP0_EntryLo0)); #if defined(TARGET_MIPS64) - if (ctx->rxi) { - TCGv tmp = tcg_temp_new(); - tcg_gen_andi_tl(tmp, arg, (3ull << CP0EnLo_XI)); - tcg_gen_shri_tl(tmp, tmp, 32); - tcg_gen_or_tl(arg, arg, tmp); - tcg_temp_free(tmp); - } + if (ctx->rxi) { + /* Move RI/XI fields to bits 31:30 */ + tcg_gen_shri_tl(arg, tmp, CP0EnLo_XI); + tcg_gen_deposit_tl(tmp, tmp, arg, 30, 2); + } #endif - tcg_gen_ext32s_tl(arg, arg); + gen_move_low32(arg, tmp); + tcg_temp_free_i64(tmp); + } rn = "EntryLo0"; break; case 1: @@ -4998,17 +5230,20 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 3: switch (sel) { case 0: - tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1)); + { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp, cpu_env, + offsetof(CPUMIPSState, CP0_EntryLo1)); #if defined(TARGET_MIPS64) - if (ctx->rxi) { - TCGv tmp = tcg_temp_new(); - tcg_gen_andi_tl(tmp, arg, (3ull << CP0EnLo_XI)); - tcg_gen_shri_tl(tmp, tmp, 32); - tcg_gen_or_tl(arg, arg, tmp); - tcg_temp_free(tmp); - } + if (ctx->rxi) { + /* Move RI/XI fields to bits 31:30 */ + tcg_gen_shri_tl(arg, tmp, CP0EnLo_XI); + tcg_gen_deposit_tl(tmp, tmp, arg, 30, 2); + } #endif - tcg_gen_ext32s_tl(arg, arg); + gen_move_low32(arg, tmp); + tcg_temp_free_i64(tmp); + } rn = "EntryLo1"; break; default: @@ -5418,7 +5653,12 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 2: case 4: case 6: - gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagLo)); + { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp, cpu_env, offsetof(CPUMIPSState, CP0_TagLo)); + gen_move_low32(arg, tmp); + tcg_temp_free_i64(tmp); + } rn = "TagLo"; break; case 1: @@ -5661,6 +5901,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_pagegrain(cpu_env, arg); rn = "PageGrain"; + ctx->bstate = BS_STOP; break; default: goto cp0_unimplemented; @@ -7557,7 +7798,7 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, if (h == 0) { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, rt); + gen_load_fpr32(ctx, fp0, rt); tcg_gen_ext_i32_tl(t0, fp0); tcg_temp_free_i32(fp0); } else { @@ -7756,7 +7997,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, TCGv_i32 fp0 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(fp0, t0); - gen_store_fpr32(fp0, rd); + gen_store_fpr32(ctx, fp0, rd); tcg_temp_free_i32(fp0); } else { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -7841,6 +8082,25 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, opn = "dmtc0"; break; #endif + case OPC_MFHC0: + check_mvh(ctx); + if (rt == 0) { + /* Treat as NOP. */ + return; + } + gen_mfhc0(ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); + opn = "mfhc0"; + break; + case OPC_MTHC0: + check_mvh(ctx); + { + TCGv t0 = tcg_temp_new(); + gen_load_gpr(t0, rt); + gen_mthc0(ctx, t0, rd, ctx->opcode & 0x7); + tcg_temp_free(t0); + } + opn = "mthc0"; + break; case OPC_MFTR: check_insn(ctx, ASE_MT); if (rd == 0) { @@ -7899,16 +8159,26 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, goto die; gen_helper_tlbr(cpu_env); break; - case OPC_ERET: - opn = "eret"; - check_insn(ctx, ISA_MIPS2); + case OPC_ERET: /* OPC_ERETNC */ if ((ctx->insn_flags & ISA_MIPS32R6) && (ctx->hflags & MIPS_HFLAG_BMASK)) { MIPS_DEBUG("CTI in delay / forbidden slot"); goto die; + } else { + int bit_shift = (ctx->hflags & MIPS_HFLAG_M16) ? 16 : 6; + if (ctx->opcode & (1 << bit_shift)) { + /* OPC_ERETNC */ + opn = "eretnc"; + check_insn(ctx, ISA_MIPS32R5); + gen_helper_eretnc(cpu_env); + } else { + /* OPC_ERET */ + opn = "eret"; + check_insn(ctx, ISA_MIPS2); + gen_helper_eret(cpu_env); + } + ctx->bstate = BS_EXCP; } - gen_helper_eret(cpu_env); - ctx->bstate = BS_EXCP; break; case OPC_DERET: opn = "deret"; @@ -8346,7 +8616,7 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); tcg_gen_ext_i32_tl(t0, fp0); tcg_temp_free_i32(fp0); } @@ -8359,7 +8629,7 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) TCGv_i32 fp0 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(fp0, t0); - gen_store_fpr32(fp0, fs); + gen_store_fpr32(ctx, fp0, fs); tcg_temp_free_i32(fp0); } opn = "mtc1"; @@ -8457,7 +8727,8 @@ static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf) gen_set_label(l1); } -static inline void gen_movcf_s (int fs, int fd, int cc, int tf) +static inline void gen_movcf_s(DisasContext *ctx, int fs, int fd, int cc, + int tf) { int cond; TCGv_i32 t0 = tcg_temp_new_i32(); @@ -8470,8 +8741,8 @@ static inline void gen_movcf_s (int fs, int fd, int cc, int tf) tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc)); tcg_gen_brcondi_i32(cond, t0, 0, l1); - gen_load_fpr32(t0, fs); - gen_store_fpr32(t0, fd); + gen_load_fpr32(ctx, t0, fs); + gen_store_fpr32(ctx, t0, fd); gen_set_label(l1); tcg_temp_free_i32(t0); } @@ -8513,8 +8784,8 @@ static inline void gen_movcf_ps(DisasContext *ctx, int fs, int fd, tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc)); tcg_gen_brcondi_i32(cond, t0, 0, l1); - gen_load_fpr32(t0, fs); - gen_store_fpr32(t0, fd); + gen_load_fpr32(ctx, t0, fs); + gen_store_fpr32(ctx, t0, fd); gen_set_label(l1); tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc+1)); @@ -8532,9 +8803,9 @@ static void gen_sel_s(DisasContext *ctx, enum fopcode op1, int fd, int ft, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); TCGv_i32 fp2 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fd); - gen_load_fpr32(fp1, ft); - gen_load_fpr32(fp2, fs); + gen_load_fpr32(ctx, fp0, fd); + gen_load_fpr32(ctx, fp1, ft); + gen_load_fpr32(ctx, fp2, fs); switch (op1) { case OPC_SEL_S: @@ -8555,7 +8826,7 @@ static void gen_sel_s(DisasContext *ctx, enum fopcode op1, int fd, int ft, break; } - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); @@ -8648,11 +8919,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_helper_float_add_s(fp0, cpu_env, fp0, fp1); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "add.s"; @@ -8663,11 +8934,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_helper_float_sub_s(fp0, cpu_env, fp0, fp1); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "sub.s"; @@ -8678,11 +8949,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_helper_float_mul_s(fp0, cpu_env, fp0, fp1); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "mul.s"; @@ -8693,11 +8964,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_helper_float_div_s(fp0, cpu_env, fp0, fp1); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "div.s"; @@ -8707,9 +8978,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_sqrt_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "sqrt.s"; @@ -8718,9 +8989,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_abs_s(fp0, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "abs.s"; @@ -8729,8 +9000,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_store_fpr32(fp0, fd); + gen_load_fpr32(ctx, fp0, fs); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "mov.s"; @@ -8739,9 +9010,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_chs_s(fp0, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "neg.s"; @@ -8752,7 +9023,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp32 = tcg_temp_new_i32(); TCGv_i64 fp64 = tcg_temp_new_i64(); - gen_load_fpr32(fp32, fs); + gen_load_fpr32(ctx, fp32, fs); gen_helper_float_roundl_s(fp64, cpu_env, fp32); tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); @@ -8766,7 +9037,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp32 = tcg_temp_new_i32(); TCGv_i64 fp64 = tcg_temp_new_i64(); - gen_load_fpr32(fp32, fs); + gen_load_fpr32(ctx, fp32, fs); gen_helper_float_truncl_s(fp64, cpu_env, fp32); tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); @@ -8780,7 +9051,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp32 = tcg_temp_new_i32(); TCGv_i64 fp64 = tcg_temp_new_i64(); - gen_load_fpr32(fp32, fs); + gen_load_fpr32(ctx, fp32, fs); gen_helper_float_ceill_s(fp64, cpu_env, fp32); tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); @@ -8794,7 +9065,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp32 = tcg_temp_new_i32(); TCGv_i64 fp64 = tcg_temp_new_i64(); - gen_load_fpr32(fp32, fs); + gen_load_fpr32(ctx, fp32, fs); gen_helper_float_floorl_s(fp64, cpu_env, fp32); tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); @@ -8806,9 +9077,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_roundw_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "round.w.s"; @@ -8817,9 +9088,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_truncw_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "trunc.w.s"; @@ -8828,9 +9099,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_ceilw_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "ceil.w.s"; @@ -8839,9 +9110,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_floorw_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "floor.w.s"; @@ -8863,7 +9134,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, break; case OPC_MOVCF_S: check_insn_opc_removed(ctx, ISA_MIPS32R6); - gen_movcf_s(fs, fd, (ft >> 2) & 0x7, ft & 0x1); + gen_movcf_s(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1); opn = "movcf.s"; break; case OPC_MOVZ_S: @@ -8876,8 +9147,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[ft], 0, l1); } fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_store_fpr32(fp0, fd); + gen_load_fpr32(ctx, fp0, fs); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); gen_set_label(l1); } @@ -8892,8 +9163,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, if (ft != 0) { tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[ft], 0, l1); fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_store_fpr32(fp0, fd); + gen_load_fpr32(ctx, fp0, fs); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); gen_set_label(l1); } @@ -8905,9 +9176,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_recip_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "recip.s"; @@ -8917,9 +9188,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_rsqrt_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "rsqrt.s"; @@ -8930,11 +9201,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); TCGv_i32 fp2 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); - gen_load_fpr32(fp2, fd); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); + gen_load_fpr32(ctx, fp2, fd); gen_helper_float_maddf_s(fp2, cpu_env, fp0, fp1, fp2); - gen_store_fpr32(fp2, fd); + gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); @@ -8947,11 +9218,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); TCGv_i32 fp2 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); - gen_load_fpr32(fp2, fd); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); + gen_load_fpr32(ctx, fp2, fd); gen_helper_float_msubf_s(fp2, cpu_env, fp0, fp1, fp2); - gen_store_fpr32(fp2, fd); + gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); @@ -8962,9 +9233,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, check_insn(ctx, ISA_MIPS32R6); { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_rint_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); opn = "rint.s"; } @@ -8973,9 +9244,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, check_insn(ctx, ISA_MIPS32R6); { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_class_s(fp0, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); opn = "class.s"; } @@ -8986,10 +9257,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); TCGv_i32 fp2 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_helper_float_min_s(fp2, cpu_env, fp0, fp1); - gen_store_fpr32(fp2, fd); + gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); @@ -9001,11 +9272,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_helper_float_recip2_s(fp0, cpu_env, fp0, fp1); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "recip2.s"; @@ -9017,10 +9288,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); TCGv_i32 fp2 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_helper_float_mina_s(fp2, cpu_env, fp0, fp1); - gen_store_fpr32(fp2, fd); + gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); @@ -9031,9 +9302,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_recip1_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "recip1.s"; @@ -9044,10 +9315,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, /* OPC_MAX_S */ TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_helper_float_max_s(fp1, cpu_env, fp0, fp1); - gen_store_fpr32(fp1, fd); + gen_store_fpr32(ctx, fp1, fd); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); opn = "max.s"; @@ -9057,9 +9328,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_rsqrt1_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "rsqrt1.s"; @@ -9070,10 +9341,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, /* OPC_MAXA_S */ TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_helper_float_maxa_s(fp1, cpu_env, fp0, fp1); - gen_store_fpr32(fp1, fd); + gen_store_fpr32(ctx, fp1, fd); tcg_temp_free_i32(fp1); tcg_temp_free_i32(fp0); opn = "maxa.s"; @@ -9084,11 +9355,11 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_helper_float_rsqrt2_s(fp0, cpu_env, fp0, fp1); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "rsqrt2.s"; @@ -9100,7 +9371,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp32 = tcg_temp_new_i32(); TCGv_i64 fp64 = tcg_temp_new_i64(); - gen_load_fpr32(fp32, fs); + gen_load_fpr32(ctx, fp32, fs); gen_helper_float_cvtd_s(fp64, cpu_env, fp32); tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); @@ -9112,9 +9383,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_cvtw_s(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "cvt.w.s"; @@ -9125,7 +9396,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp32 = tcg_temp_new_i32(); TCGv_i64 fp64 = tcg_temp_new_i64(); - gen_load_fpr32(fp32, fs); + gen_load_fpr32(ctx, fp32, fs); gen_helper_float_cvtl_s(fp64, cpu_env, fp32); tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); @@ -9141,8 +9412,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp32_0 = tcg_temp_new_i32(); TCGv_i32 fp32_1 = tcg_temp_new_i32(); - gen_load_fpr32(fp32_0, fs); - gen_load_fpr32(fp32_1, ft); + gen_load_fpr32(ctx, fp32_0, fs); + gen_load_fpr32(ctx, fp32_1, ft); tcg_gen_concat_i32_i64(fp64, fp32_1, fp32_0); tcg_temp_free_i32(fp32_1); tcg_temp_free_i32(fp32_0); @@ -9344,7 +9615,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); gen_helper_float_roundw_d(fp32, cpu_env, fp64); tcg_temp_free_i64(fp64); - gen_store_fpr32(fp32, fd); + gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } opn = "round.w.d"; @@ -9358,7 +9629,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); gen_helper_float_truncw_d(fp32, cpu_env, fp64); tcg_temp_free_i64(fp64); - gen_store_fpr32(fp32, fd); + gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } opn = "trunc.w.d"; @@ -9372,7 +9643,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); gen_helper_float_ceilw_d(fp32, cpu_env, fp64); tcg_temp_free_i64(fp64); - gen_store_fpr32(fp32, fd); + gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } opn = "ceil.w.d"; @@ -9386,7 +9657,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); gen_helper_float_floorw_d(fp32, cpu_env, fp64); tcg_temp_free_i64(fp64); - gen_store_fpr32(fp32, fd); + gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } opn = "floor.w.d"; @@ -9669,7 +9940,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); gen_helper_float_cvts_d(fp32, cpu_env, fp64); tcg_temp_free_i64(fp64); - gen_store_fpr32(fp32, fd); + gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } opn = "cvt.s.d"; @@ -9683,7 +9954,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); gen_helper_float_cvtw_d(fp32, cpu_env, fp64); tcg_temp_free_i64(fp64); - gen_store_fpr32(fp32, fd); + gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } opn = "cvt.w.d"; @@ -9704,9 +9975,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_cvts_w(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "cvt.s.w"; @@ -9717,7 +9988,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp32 = tcg_temp_new_i32(); TCGv_i64 fp64 = tcg_temp_new_i64(); - gen_load_fpr32(fp32, fs); + gen_load_fpr32(ctx, fp32, fs); gen_helper_float_cvtd_w(fp64, cpu_env, fp32); tcg_temp_free_i32(fp32); gen_store_fpr64(ctx, fp64, fd); @@ -9734,7 +10005,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_load_fpr64(ctx, fp64, fs); gen_helper_float_cvts_l(fp32, cpu_env, fp64); tcg_temp_free_i64(fp64); - gen_store_fpr32(fp32, fd); + gen_store_fpr32(ctx, fp32, fd); tcg_temp_free_i32(fp32); } opn = "cvt.s.l"; @@ -9973,7 +10244,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_load_fpr32h(ctx, fp0, fs); gen_helper_float_cvts_pu(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "cvt.s.pu"; @@ -9995,9 +10266,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_helper_float_cvts_pl(fp0, cpu_env, fp0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "cvt.s.pl"; @@ -10008,10 +10279,10 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); gen_store_fpr32h(ctx, fp0, fd); - gen_store_fpr32(fp1, fd); + gen_store_fpr32(ctx, fp1, fd); tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); } @@ -10023,9 +10294,9 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); gen_load_fpr32h(ctx, fp1, ft); - gen_store_fpr32(fp1, fd); + gen_store_fpr32(ctx, fp1, fd); gen_store_fpr32h(ctx, fp0, fd); tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); @@ -10039,8 +10310,8 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, TCGv_i32 fp1 = tcg_temp_new_i32(); gen_load_fpr32h(ctx, fp0, fs); - gen_load_fpr32(fp1, ft); - gen_store_fpr32(fp1, fd); + gen_load_fpr32(ctx, fp1, ft); + gen_store_fpr32(ctx, fp1, fd); gen_store_fpr32h(ctx, fp0, fd); tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); @@ -10055,7 +10326,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1, gen_load_fpr32h(ctx, fp0, fs); gen_load_fpr32h(ctx, fp1, ft); - gen_store_fpr32(fp1, fd); + gen_store_fpr32(ctx, fp1, fd); gen_store_fpr32h(ctx, fp0, fd); tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); @@ -10130,7 +10401,7 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL); tcg_gen_trunc_tl_i32(fp0, t0); - gen_store_fpr32(fp0, fd); + gen_store_fpr32(ctx, fp0, fd); tcg_temp_free_i32(fp0); } opn = "lwxc1"; @@ -10162,7 +10433,7 @@ static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, check_cop1x(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); + gen_load_fpr32(ctx, fp0, fs); tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL); tcg_temp_free_i32(fp0); } @@ -10219,23 +10490,23 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, tcg_gen_andi_tl(t0, t0, 0x7); tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0, l1); - gen_load_fpr32(fp, fs); + gen_load_fpr32(ctx, fp, fs); gen_load_fpr32h(ctx, fph, fs); - gen_store_fpr32(fp, fd); + gen_store_fpr32(ctx, fp, fd); gen_store_fpr32h(ctx, fph, fd); tcg_gen_br(l2); gen_set_label(l1); tcg_gen_brcondi_tl(TCG_COND_NE, t0, 4, l2); tcg_temp_free(t0); #ifdef TARGET_WORDS_BIGENDIAN - gen_load_fpr32(fp, fs); + gen_load_fpr32(ctx, fp, fs); gen_load_fpr32h(ctx, fph, ft); gen_store_fpr32h(ctx, fp, fd); - gen_store_fpr32(fph, fd); + gen_store_fpr32(ctx, fph, fd); #else gen_load_fpr32h(ctx, fph, fs); - gen_load_fpr32(fp, ft); - gen_store_fpr32(fph, fd); + gen_load_fpr32(ctx, fp, ft); + gen_store_fpr32(ctx, fph, fd); gen_store_fpr32h(ctx, fp, fd); #endif gen_set_label(l2); @@ -10251,13 +10522,13 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, TCGv_i32 fp1 = tcg_temp_new_i32(); TCGv_i32 fp2 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); - gen_load_fpr32(fp2, fr); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); + gen_load_fpr32(ctx, fp2, fr); gen_helper_float_madd_s(fp2, cpu_env, fp0, fp1, fp2); tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp2, fd); + gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); } opn = "madd.s"; @@ -10306,13 +10577,13 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, TCGv_i32 fp1 = tcg_temp_new_i32(); TCGv_i32 fp2 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); - gen_load_fpr32(fp2, fr); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); + gen_load_fpr32(ctx, fp2, fr); gen_helper_float_msub_s(fp2, cpu_env, fp0, fp1, fp2); tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp2, fd); + gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); } opn = "msub.s"; @@ -10361,13 +10632,13 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, TCGv_i32 fp1 = tcg_temp_new_i32(); TCGv_i32 fp2 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); - gen_load_fpr32(fp2, fr); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); + gen_load_fpr32(ctx, fp2, fr); gen_helper_float_nmadd_s(fp2, cpu_env, fp0, fp1, fp2); tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp2, fd); + gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); } opn = "nmadd.s"; @@ -10416,13 +10687,13 @@ static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, TCGv_i32 fp1 = tcg_temp_new_i32(); TCGv_i32 fp2 = tcg_temp_new_i32(); - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); - gen_load_fpr32(fp2, fr); + gen_load_fpr32(ctx, fp0, fs); + gen_load_fpr32(ctx, fp1, ft); + gen_load_fpr32(ctx, fp2, fr); gen_helper_float_nmsub_s(fp2, cpu_env, fp0, fp1, fp2); tcg_temp_free_i32(fp0); tcg_temp_free_i32(fp1); - gen_store_fpr32(fp2, fd); + gen_store_fpr32(ctx, fp2, fd); tcg_temp_free_i32(fp2); } opn = "nmsub.s"; @@ -13502,7 +13773,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case MOVF_FMT: switch (fmt) { case FMT_SDPS_S: - gen_movcf_s(rs, rt, cc, 0); + gen_movcf_s(ctx, rs, rt, cc, 0); break; case FMT_SDPS_D: gen_movcf_d(ctx, rs, rt, cc, 0); @@ -13517,7 +13788,7 @@ static void decode_micromips32_opc (CPUMIPSState *env, DisasContext *ctx, case MOVT_FMT: switch (fmt) { case FMT_SDPS_S: - gen_movcf_s(rs, rt, cc, 1); + gen_movcf_s(ctx, rs, rt, cc, 1); break; case FMT_SDPS_D: gen_movcf_d(ctx, rs, rt, cc, 1); @@ -18404,32 +18675,39 @@ static void gen_msa(CPUMIPSState *env, DisasContext *ctx) uint8_t wd = (ctx->opcode >> 6) & 0x1f; uint8_t df = (ctx->opcode >> 0) & 0x3; - TCGv_i32 tdf = tcg_const_i32(df); TCGv_i32 twd = tcg_const_i32(wd); - TCGv_i32 trs = tcg_const_i32(rs); - TCGv_i32 ts10 = tcg_const_i32(s10); + TCGv taddr = tcg_temp_new(); + gen_base_offset_addr(ctx, taddr, rs, s10 << df); switch (MASK_MSA_MINOR(opcode)) { case OPC_LD_B: + gen_helper_msa_ld_b(cpu_env, twd, taddr); + break; case OPC_LD_H: + gen_helper_msa_ld_h(cpu_env, twd, taddr); + break; case OPC_LD_W: + gen_helper_msa_ld_w(cpu_env, twd, taddr); + break; case OPC_LD_D: - save_cpu_state(ctx, 1); - gen_helper_msa_ld_df(cpu_env, tdf, twd, trs, ts10); + gen_helper_msa_ld_d(cpu_env, twd, taddr); break; case OPC_ST_B: + gen_helper_msa_st_b(cpu_env, twd, taddr); + break; case OPC_ST_H: + gen_helper_msa_st_h(cpu_env, twd, taddr); + break; case OPC_ST_W: + gen_helper_msa_st_w(cpu_env, twd, taddr); + break; case OPC_ST_D: - save_cpu_state(ctx, 1); - gen_helper_msa_st_df(cpu_env, tdf, twd, trs, ts10); + gen_helper_msa_st_d(cpu_env, twd, taddr); break; } tcg_temp_free_i32(twd); - tcg_temp_free_i32(tdf); - tcg_temp_free_i32(trs); - tcg_temp_free_i32(ts10); + tcg_temp_free(taddr); } break; default: @@ -18564,6 +18842,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) case OPC_MTC0: case OPC_MFTR: case OPC_MTTR: + case OPC_MFHC0: + case OPC_MTHC0: #if defined(TARGET_MIPS64) case OPC_DMFC0: case OPC_DMTC0: @@ -19134,6 +19414,9 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, ctx.ie = (env->CP0_Config4 >> CP0C4_IE) & 3; ctx.bi = (env->CP0_Config3 >> CP0C3_BI) & 1; ctx.bp = (env->CP0_Config3 >> CP0C3_BP) & 1; + ctx.PAMask = env->PAMask; + ctx.mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1; + ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift; /* Restore delay slot state from the tb context. */ ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */ ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; @@ -19143,6 +19426,8 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, #else ctx.mem_idx = ctx.hflags & MIPS_HFLAG_KSU; #endif + ctx.default_tcg_memop_mask = (ctx.insn_flags & ISA_MIPS32R6) ? + MO_UNALN : MO_ALIGN; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) @@ -19385,7 +19670,8 @@ void mips_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, "CP0 Status 0x%08x Cause 0x%08x EPC 0x" TARGET_FMT_lx "\n", env->CP0_Status, env->CP0_Cause, env->CP0_EPC); - cpu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x" TARGET_FMT_lx "\n", + cpu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x%016" + PRIx64 "\n", env->CP0_Config0, env->CP0_Config1, env->lladdr); cpu_fprintf(f, " Config2 0x%08x Config3 0x%08x\n", env->CP0_Config2, env->CP0_Config3); @@ -19519,7 +19805,6 @@ void cpu_state_reset(CPUMIPSState *env) } #endif env->PABITS = env->cpu_model->PABITS; - env->PAMask = (target_ulong)((1ULL << env->cpu_model->PABITS) - 1); env->CP0_SRSConf0_rw_bitmask = env->cpu_model->CP0_SRSConf0_rw_bitmask; env->CP0_SRSConf0 = env->cpu_model->CP0_SRSConf0; env->CP0_SRSConf1_rw_bitmask = env->cpu_model->CP0_SRSConf1_rw_bitmask; @@ -19640,6 +19925,7 @@ void cpu_state_reset(CPUMIPSState *env) compute_hflags(env); restore_rounding_mode(env); restore_flush_mode(env); + restore_pamask(env); cs->exception_index = EXCP_NONE; } diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index 85a65e7..30605da 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -400,10 +400,12 @@ static const mips_def_t mips_defs[] = (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | (1 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, - .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP), + .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP) | + (1 << CP0C3_LPA), .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M), .CP0_Config4_rw_bitmask = 0, - .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_UFR), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_UFR) | (1 << CP0C5_LLB) | + (1 << CP0C5_MVH), .CP0_Config5_rw_bitmask = (0 << CP0C5_M) | (1 << CP0C5_K) | (1 << CP0C5_CV) | (0 << CP0C5_EVA) | (1 << CP0C5_MSAEn) | (1 << CP0C5_UFR) | @@ -413,11 +415,12 @@ static const mips_def_t mips_defs[] = .SYNCI_Step = 32, .CCRes = 2, .CP0_Status_rw_bitmask = 0x3778FF1F, + .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA), .CP1_fcr0 = (1 << FCR0_UFRP) | (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | (0x93 << FCR0_PRID), .SEGBITS = 32, - .PABITS = 32, + .PABITS = 40, .insn_flags = CPU_MIPS32R5 | ASE_MIPS16 | ASE_MSA, .mmu_type = MMU_TYPE_R4000, }, @@ -553,9 +556,6 @@ static const mips_def_t mips_defs[] = (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), .SEGBITS = 42, - /* The architectural limit is 59, but we have hardcoded 36 bit - in some places... - .PABITS = 59, */ /* the architectural limit */ .PABITS = 36, .insn_flags = CPU_MIPS64R2 | ASE_MIPS3D, .mmu_type = MMU_TYPE_R4000, @@ -607,7 +607,7 @@ static const mips_def_t mips_defs[] = }, { /* A generic CPU supporting MIPS64 Release 6 ISA. - FIXME: Support IEEE 754-2008 FP and misaligned memory accesses. + FIXME: Support IEEE 754-2008 FP. Eventually this should be replaced by a real CPU model. */ .name = "MIPS64R6-generic", .CP0_PRid = 0x00010000, @@ -619,10 +619,13 @@ static const mips_def_t mips_defs[] = (0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_RXI) | (1 << CP0C3_BP) | - (1 << CP0C3_BI) | (1 << CP0C3_ULRI) | (1U << CP0C3_M), + (1 << CP0C3_BI) | (1 << CP0C3_ULRI) | (1 << CP0C3_LPA) | + (1U << CP0C3_M), .CP0_Config4 = MIPS_CONFIG4 | (0xfc << CP0C4_KScrExist) | (3 << CP0C4_IE) | (1 << CP0C4_M), - .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_LLB), + .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | + (1 << CP0C5_UFE), .CP0_LLAddr_rw_bitmask = 0, .CP0_LLAddr_shift = 0, .SYNCI_Step = 32, @@ -630,15 +633,12 @@ static const mips_def_t mips_defs[] = .CP0_Status_rw_bitmask = 0x30D8FFFF, .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | (1U << CP0PG_RIE), - .CP0_PageGrain_rw_bitmask = 0, - .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | - (1 << FCR0_D) | (1 << FCR0_S) | (0x00 << FCR0_PRID) | - (0x0 << FCR0_REV), + .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA), + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_F64) | (1 << FCR0_L) | + (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | + (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), .SEGBITS = 42, - /* The architectural limit is 59, but we have hardcoded 36 bit - in some places... - .PABITS = 59, */ /* the architectural limit */ - .PABITS = 36, + .PABITS = 48, .insn_flags = CPU_MIPS64R6, .mmu_type = MMU_TYPE_R4000, }, @@ -701,9 +701,6 @@ static const mips_def_t mips_defs[] = (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), .SEGBITS = 42, - /* The architectural limit is 59, but we have hardcoded 36 bit - in some places... - .PABITS = 59, */ /* the architectural limit */ .PABITS = 36, .insn_flags = CPU_MIPS64R2 | ASE_DSP | ASE_DSPR2, .mmu_type = MMU_TYPE_R4000, diff --git a/target-ppc/machine.c b/target-ppc/machine.c index d875211..f4ac761 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -213,6 +213,7 @@ static const VMStateDescription vmstate_fpu = { .name = "cpu/fpu", .version_id = 1, .minimum_version_id = 1, + .needed = fpu_needed, .fields = (VMStateField[]) { VMSTATE_FLOAT64_ARRAY(env.fpr, PowerPCCPU, 32), VMSTATE_UINTTL(env.fpscr, PowerPCCPU), @@ -231,6 +232,7 @@ static const VMStateDescription vmstate_altivec = { .name = "cpu/altivec", .version_id = 1, .minimum_version_id = 1, + .needed = altivec_needed, .fields = (VMStateField[]) { VMSTATE_AVR_ARRAY(env.avr, PowerPCCPU, 32), VMSTATE_UINT32(env.vscr, PowerPCCPU), @@ -249,6 +251,7 @@ static const VMStateDescription vmstate_vsx = { .name = "cpu/vsx", .version_id = 1, .minimum_version_id = 1, + .needed = vsx_needed, .fields = (VMStateField[]) { VMSTATE_UINT64_ARRAY(env.vsr, PowerPCCPU, 32), VMSTATE_END_OF_LIST() @@ -269,6 +272,7 @@ static const VMStateDescription vmstate_tm = { .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, + .needed = tm_needed, .fields = (VMStateField []) { VMSTATE_UINTTL_ARRAY(env.tm_gpr, PowerPCCPU, 32), VMSTATE_AVR_ARRAY(env.tm_vsr, PowerPCCPU, 64), @@ -302,6 +306,7 @@ static const VMStateDescription vmstate_sr = { .name = "cpu/sr", .version_id = 1, .minimum_version_id = 1, + .needed = sr_needed, .fields = (VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.sr, PowerPCCPU, 32), VMSTATE_END_OF_LIST() @@ -351,6 +356,7 @@ static const VMStateDescription vmstate_slb = { .name = "cpu/slb", .version_id = 1, .minimum_version_id = 1, + .needed = slb_needed, .fields = (VMStateField[]) { VMSTATE_INT32_EQUAL(env.slb_nr, PowerPCCPU), VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), @@ -383,6 +389,7 @@ static const VMStateDescription vmstate_tlb6xx = { .name = "cpu/tlb6xx", .version_id = 1, .minimum_version_id = 1, + .needed = tlb6xx_needed, .fields = (VMStateField[]) { VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU), VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlb6, PowerPCCPU, @@ -429,6 +436,7 @@ static const VMStateDescription vmstate_pbr403 = { .name = "cpu/pbr403", .version_id = 1, .minimum_version_id = 1, + .needed = pbr403_needed, .fields = (VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.pb, PowerPCCPU, 4), VMSTATE_END_OF_LIST() @@ -439,6 +447,7 @@ static const VMStateDescription vmstate_tlbemb = { .name = "cpu/tlb6xx", .version_id = 1, .minimum_version_id = 1, + .needed = tlbemb_needed, .fields = (VMStateField[]) { VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU), VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlbe, PowerPCCPU, @@ -448,13 +457,9 @@ static const VMStateDescription vmstate_tlbemb = { /* 403 protection registers */ VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_pbr403, - .needed = pbr403_needed, - } , { - /* empty */ - } + .subsections = (const VMStateDescription*[]) { + &vmstate_pbr403, + NULL } }; @@ -483,6 +488,7 @@ static const VMStateDescription vmstate_tlbmas = { .name = "cpu/tlbmas", .version_id = 1, .minimum_version_id = 1, + .needed = tlbmas_needed, .fields = (VMStateField[]) { VMSTATE_INT32_EQUAL(env.nb_tlb, PowerPCCPU), VMSTATE_STRUCT_VARRAY_POINTER_INT32(env.tlb.tlbm, PowerPCCPU, @@ -533,38 +539,18 @@ const VMStateDescription vmstate_ppc_cpu = { VMSTATE_UINT32_EQUAL(env.nb_BATs, PowerPCCPU), VMSTATE_END_OF_LIST() }, - .subsections = (VMStateSubsection []) { - { - .vmsd = &vmstate_fpu, - .needed = fpu_needed, - } , { - .vmsd = &vmstate_altivec, - .needed = altivec_needed, - } , { - .vmsd = &vmstate_vsx, - .needed = vsx_needed, - } , { - .vmsd = &vmstate_sr, - .needed = sr_needed, - } , { + .subsections = (const VMStateDescription*[]) { + &vmstate_fpu, + &vmstate_altivec, + &vmstate_vsx, + &vmstate_sr, #ifdef TARGET_PPC64 - .vmsd = &vmstate_tm, - .needed = tm_needed, - } , { - .vmsd = &vmstate_slb, - .needed = slb_needed, - } , { + &vmstate_tm, + &vmstate_slb, #endif /* TARGET_PPC64 */ - .vmsd = &vmstate_tlb6xx, - .needed = tlb6xx_needed, - } , { - .vmsd = &vmstate_tlbemb, - .needed = tlbemb_needed, - } , { - .vmsd = &vmstate_tlbmas, - .needed = tlbmas_needed, - } , { - /* empty */ - } + &vmstate_tlb6xx, + &vmstate_tlbemb, + &vmstate_tlbmas, + NULL } }; diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index 584e74b..d63eb51 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -1100,6 +1100,7 @@ uint32_t set_cc_nz_f128(float128 v); /* misc_helper.c */ #ifndef CONFIG_USER_ONLY +int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3); void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3); #endif void program_interrupt(CPUS390XState *env, uint32_t code, int ilen); diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index f6f61b9..b02ff8d 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -98,6 +98,7 @@ #define PRIV_E3_MPCIFC 0xd0 #define PRIV_E3_STPCIFC 0xd4 +#define DIAG_TIMEREVENT 0x288 #define DIAG_IPL 0x308 #define DIAG_KVM_HYPERCALL 0x500 #define DIAG_KVM_BREAKPOINT 0x501 @@ -1267,6 +1268,20 @@ static int handle_hypercall(S390CPU *cpu, struct kvm_run *run) return ret; } +static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run) +{ + uint64_t r1, r3; + int rc; + + cpu_synchronize_state(CPU(cpu)); + r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; + r3 = run->s390_sieic.ipa & 0x000f; + rc = handle_diag_288(&cpu->env, r1, r3); + if (rc) { + enter_pgmcheck(cpu, PGM_SPECIFICATION); + } +} + static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run) { uint64_t r1, r3; @@ -1306,6 +1321,9 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) */ func_code = decode_basedisp_rs(&cpu->env, ipb, NULL) & DIAG_KVM_CODE_MASK; switch (func_code) { + case DIAG_TIMEREVENT: + kvm_handle_diag_288(cpu, run); + break; case DIAG_IPL: kvm_handle_diag_308(cpu, run); break; diff --git a/target-s390x/machine.c b/target-s390x/machine.c index 0044749..b76fb08 100644 --- a/target-s390x/machine.c +++ b/target-s390x/machine.c @@ -42,10 +42,17 @@ static void cpu_pre_save(void *opaque) } } +static inline bool fpu_needed(void *opaque) +{ + /* This looks odd, but we might want to NOT transfer fprs in the future */ + return true; +} + const VMStateDescription vmstate_fpu = { .name = "cpu/fpu", .version_id = 1, .minimum_version_id = 1, + .needed = fpu_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(env.vregs[0][0].ll, S390CPU), VMSTATE_UINT64(env.vregs[1][0].ll, S390CPU), @@ -68,16 +75,11 @@ const VMStateDescription vmstate_fpu = { } }; -static inline bool fpu_needed(void *opaque) -{ - /* This looks odd, but we might want to NOT transfer fprs in the future */ - return true; -} - const VMStateDescription vmstate_vregs = { .name = "cpu/vregs", .version_id = 1, .minimum_version_id = 1, + .needed = vregs_needed, .fields = (VMStateField[]) { /* vregs[0][0] -> vregs[15][0] and fregs are overlays */ VMSTATE_UINT64(env.vregs[16][0].ll, S390CPU), @@ -159,16 +161,10 @@ const VMStateDescription vmstate_s390_cpu = { VMSTATE_VBUFFER_UINT32(irqstate, S390CPU, 4, NULL, 0, irqstate_saved_size), VMSTATE_END_OF_LIST() - }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &vmstate_fpu, - .needed = fpu_needed, - } , { - .vmsd = &vmstate_vregs, - .needed = vregs_needed, - } , { - /* empty */ - } + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_fpu, + &vmstate_vregs, + NULL }, }; diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c index b375ab7..6711504 100644 --- a/target-s390x/misc_helper.c +++ b/target-s390x/misc_helper.c @@ -30,6 +30,7 @@ #include <linux/kvm.h> #endif #include "exec/cpu_ldst.h" +#include "hw/watchdog/wdt_diag288.h" #if !defined(CONFIG_USER_ONLY) #include "sysemu/cpus.h" @@ -153,6 +154,34 @@ static int load_normal_reset(S390CPU *cpu) return 0; } +int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) +{ + uint64_t func = env->regs[r1]; + uint64_t timeout = env->regs[r1 + 1]; + uint64_t action = env->regs[r3]; + Object *obj; + DIAG288State *diag288; + DIAG288Class *diag288_class; + + if (r1 % 2 || action != 0) { + return -1; + } + + /* Timeout must be more than 15 seconds except for timer deletion */ + if (func != WDT_DIAG288_CANCEL && timeout < 15) { + return -1; + } + + obj = object_resolve_path_type("", TYPE_WDT_DIAG288, NULL); + if (!obj) { + return -1; + } + + diag288 = DIAG288(obj); + diag288_class = DIAG288_GET_CLASS(diag288); + return diag288_class->handle_timer(diag288, func, timeout); +} + #define DIAG_308_RC_OK 0x0001 #define DIAG_308_RC_NO_CONF 0x0102 #define DIAG_308_RC_INVALID 0x0402 diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c index d187a2b..cccb14f 100644 --- a/target-sh4/cpu.c +++ b/target-sh4/cpu.c @@ -61,7 +61,8 @@ static void superh_cpu_reset(CPUState *s) env->fpscr = FPSCR_PR; /* value for userspace according to the kernel */ set_float_rounding_mode(float_round_nearest_even, &env->fp_status); /* ?! */ #else - env->sr = SR_MD | SR_RB | SR_BL | SR_I3 | SR_I2 | SR_I1 | SR_I0; + env->sr = (1u << SR_MD) | (1u << SR_RB) | (1u << SR_BL) | + (1u << SR_I3) | (1u << SR_I2) | (1u << SR_I1) | (1u << SR_I0); env->fpscr = FPSCR_DN | FPSCR_RM_ZERO; /* CPU reset value according to SH4 manual */ set_float_rounding_mode(float_round_to_zero, &env->fp_status); set_flush_to_zero(1, &env->fp_status); diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h index c8dea6c..4a027a6 100644 --- a/target-sh4/cpu.h +++ b/target-sh4/cpu.h @@ -47,18 +47,18 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -#define SR_MD (1 << 30) -#define SR_RB (1 << 29) -#define SR_BL (1 << 28) -#define SR_FD (1 << 15) -#define SR_M (1 << 9) -#define SR_Q (1 << 8) -#define SR_I3 (1 << 7) -#define SR_I2 (1 << 6) -#define SR_I1 (1 << 5) -#define SR_I0 (1 << 4) -#define SR_S (1 << 1) -#define SR_T (1 << 0) +#define SR_MD 30 +#define SR_RB 29 +#define SR_BL 28 +#define SR_FD 15 +#define SR_M 9 +#define SR_Q 8 +#define SR_I3 7 +#define SR_I2 6 +#define SR_I1 5 +#define SR_I0 4 +#define SR_S 1 +#define SR_T 0 #define FPSCR_MASK (0x003fffff) #define FPSCR_FR (1 << 21) @@ -138,7 +138,10 @@ typedef struct CPUSH4State { uint32_t flags; /* general execution flags */ uint32_t gregs[24]; /* general registers */ float32 fregs[32]; /* floating point registers */ - uint32_t sr; /* status register */ + uint32_t sr; /* status register (with T split out) */ + uint32_t sr_m; /* M bit of status register */ + uint32_t sr_q; /* Q bit of status register */ + uint32_t sr_t; /* T bit of status register */ uint32_t ssr; /* saved status register */ uint32_t spc; /* saved program counter */ uint32_t gbr; /* global base register */ @@ -234,7 +237,7 @@ void cpu_load_tlb(CPUSH4State * env); #define MMU_USER_IDX 1 static inline int cpu_mmu_index (CPUSH4State *env) { - return (env->sr & SR_MD) == 0 ? 1 : 0; + return (env->sr & (1u << SR_MD)) == 0 ? 1 : 0; } #include "exec/cpu-all.h" @@ -331,6 +334,21 @@ static inline int cpu_ptel_pr (uint32_t ptel) #define TB_FLAG_PENDING_MOVCA (1 << 4) +static inline target_ulong cpu_read_sr(CPUSH4State *env) +{ + return env->sr | (env->sr_m << SR_M) | + (env->sr_q << SR_Q) | + (env->sr_t << SR_T); +} + +static inline void cpu_write_sr(CPUSH4State *env, target_ulong sr) +{ + env->sr_m = (sr >> SR_M) & 1; + env->sr_q = (sr >> SR_Q) & 1; + env->sr_t = (sr >> SR_T) & 1; + env->sr = sr & ~((1u << SR_M) | (1u << SR_Q) | (1u << SR_T)); +} + static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -339,8 +357,8 @@ static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc, *flags = (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL | DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME)) /* Bits 0- 3 */ | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR)) /* Bits 19-21 */ - | (env->sr & (SR_MD | SR_RB)) /* Bits 29-30 */ - | (env->sr & SR_FD) /* Bit 15 */ + | (env->sr & ((1u << SR_MD) | (1u << SR_RB))) /* Bits 29-30 */ + | (env->sr & (1u << SR_FD)) /* Bit 15 */ | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 4 */ } diff --git a/target-sh4/gdbstub.c b/target-sh4/gdbstub.c index df4fa2a..a365a27 100644 --- a/target-sh4/gdbstub.c +++ b/target-sh4/gdbstub.c @@ -31,7 +31,7 @@ int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) switch (n) { case 0 ... 7: - if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) { + if ((env->sr & (1u << SR_MD)) && (env->sr & (1u << SR_RB))) { return gdb_get_regl(mem_buf, env->gregs[n + 16]); } else { return gdb_get_regl(mem_buf, env->gregs[n]); @@ -51,7 +51,7 @@ int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) case 21: return gdb_get_regl(mem_buf, env->macl); case 22: - return gdb_get_regl(mem_buf, env->sr); + return gdb_get_regl(mem_buf, cpu_read_sr(env)); case 23: return gdb_get_regl(mem_buf, env->fpul); case 24: @@ -83,7 +83,7 @@ int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) switch (n) { case 0 ... 7: - if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) { + if ((env->sr & (1u << SR_MD)) && (env->sr & (1u << SR_RB))) { env->gregs[n + 16] = ldl_p(mem_buf); } else { env->gregs[n] = ldl_p(mem_buf); @@ -111,7 +111,7 @@ int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->macl = ldl_p(mem_buf); break; case 22: - env->sr = ldl_p(mem_buf); + cpu_write_sr(env, ldl_p(mem_buf)); break; case 23: env->fpul = ldl_p(mem_buf); diff --git a/target-sh4/helper.c b/target-sh4/helper.c index 5811360..a533f08 100644 --- a/target-sh4/helper.c +++ b/target-sh4/helper.c @@ -93,7 +93,7 @@ void superh_cpu_do_interrupt(CPUState *cs) do_exp = cs->exception_index != -1; do_irq = do_irq && (cs->exception_index == -1); - if (env->sr & SR_BL) { + if (env->sr & (1u << SR_BL)) { if (do_exp && cs->exception_index != 0x1e0) { cs->exception_index = 0x000; /* masked exception -> reset */ } @@ -162,10 +162,10 @@ void superh_cpu_do_interrupt(CPUState *cs) log_cpu_state(cs, 0); } - env->ssr = env->sr; + env->ssr = cpu_read_sr(env); env->spc = env->pc; env->sgr = env->gregs[15]; - env->sr |= SR_BL | SR_MD | SR_RB; + env->sr |= (1u << SR_BL) | (1u << SR_MD) | (1u << SR_RB); if (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { /* Branch instruction should be executed again before delay slot. */ @@ -182,7 +182,7 @@ void superh_cpu_do_interrupt(CPUState *cs) case 0x000: case 0x020: case 0x140: - env->sr &= ~SR_FD; + env->sr &= ~(1u << SR_FD); env->sr |= 0xf << 4; /* IMASK */ env->pc = 0xa0000000; break; @@ -355,23 +355,24 @@ static int get_mmu_address(CPUSH4State * env, target_ulong * physical, int use_asid, n; tlb_t *matching = NULL; - use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0; + use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD)); if (rw == 2) { n = find_itlb_entry(env, address, use_asid); if (n >= 0) { matching = &env->itlb[n]; - if (!(env->sr & SR_MD) && !(matching->pr & 2)) + if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) { n = MMU_ITLB_VIOLATION; - else + } else { *prot = PAGE_EXEC; + } } else { n = find_utlb_entry(env, address, use_asid); if (n >= 0) { n = copy_utlb_entry_itlb(env, n); matching = &env->itlb[n]; - if (!(env->sr & SR_MD) && !(matching->pr & 2)) { - n = MMU_ITLB_VIOLATION; + if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) { + n = MMU_ITLB_VIOLATION; } else { *prot = PAGE_READ | PAGE_EXEC; if ((matching->pr & 1) && matching->d) { @@ -388,7 +389,7 @@ static int get_mmu_address(CPUSH4State * env, target_ulong * physical, n = find_utlb_entry(env, address, use_asid); if (n >= 0) { matching = &env->utlb[n]; - if (!(env->sr & SR_MD) && !(matching->pr & 2)) { + if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) { n = (rw == 1) ? MMU_DTLB_VIOLATION_WRITE : MMU_DTLB_VIOLATION_READ; } else if ((rw == 1) && !(matching->pr & 1)) { @@ -421,7 +422,7 @@ static int get_physical_address(CPUSH4State * env, target_ulong * physical, /* P1, P2 and P4 areas do not use translation */ if ((address >= 0x80000000 && address < 0xc0000000) || address >= 0xe0000000) { - if (!(env->sr & SR_MD) + if (!(env->sr & (1u << SR_MD)) && (address < 0xe0000000 || address >= 0xe4000000)) { /* Unauthorized access in user mode (only store queues are available) */ fprintf(stderr, "Unauthorized access\n"); @@ -690,7 +691,7 @@ void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr, uint8_t d = (uint8_t)((mem_value & 0x00000200) >> 9); uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8); uint8_t asid = (uint8_t)(mem_value & 0x000000ff); - int use_asid = (s->mmucr & MMUCR_SV) == 0 || (s->sr & SR_MD) == 0; + int use_asid = !(s->mmucr & MMUCR_SV) || !(s->sr & (1u << SR_MD)); if (associate) { int i; @@ -821,10 +822,10 @@ void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr, int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) { int n; - int use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0; + int use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD)); /* check area */ - if (env->sr & SR_MD) { + if (env->sr & (1u << SR_MD)) { /* For previledged mode, P2 and P4 area is not cachable. */ if ((0xA0000000 <= addr && addr < 0xC0000000) || 0xE0000000 <= addr) return 0; diff --git a/target-sh4/helper.h b/target-sh4/helper.h index 3b5c436..c9bc407 100644 --- a/target-sh4/helper.h +++ b/target-sh4/helper.h @@ -11,7 +11,6 @@ DEF_HELPER_3(movcal, void, env, i32, i32) DEF_HELPER_1(discard_movcal_backup, void, env) DEF_HELPER_2(ocbi, void, env, i32) -DEF_HELPER_3(div1, i32, env, i32, i32) DEF_HELPER_3(macl, void, env, i32, i32) DEF_HELPER_3(macw, void, env, i32, i32) diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c index 74a5c4e..cbc11ae 100644 --- a/target-sh4/op_helper.c +++ b/target-sh4/op_helper.c @@ -156,124 +156,6 @@ void helper_ocbi(CPUSH4State *env, uint32_t address) } } -#define T (env->sr & SR_T) -#define Q (env->sr & SR_Q ? 1 : 0) -#define M (env->sr & SR_M ? 1 : 0) -#define SETT env->sr |= SR_T -#define CLRT env->sr &= ~SR_T -#define SETQ env->sr |= SR_Q -#define CLRQ env->sr &= ~SR_Q -#define SETM env->sr |= SR_M -#define CLRM env->sr &= ~SR_M - -uint32_t helper_div1(CPUSH4State *env, uint32_t arg0, uint32_t arg1) -{ - uint32_t tmp0, tmp2; - uint8_t old_q, tmp1 = 0xff; - - //printf("div1 arg0=0x%08x arg1=0x%08x M=%d Q=%d T=%d\n", arg0, arg1, M, Q, T); - old_q = Q; - if ((0x80000000 & arg1) != 0) - SETQ; - else - CLRQ; - tmp2 = arg0; - arg1 <<= 1; - arg1 |= T; - switch (old_q) { - case 0: - switch (M) { - case 0: - tmp0 = arg1; - arg1 -= tmp2; - tmp1 = arg1 > tmp0; - switch (Q) { - case 0: - if (tmp1) - SETQ; - else - CLRQ; - break; - case 1: - if (tmp1 == 0) - SETQ; - else - CLRQ; - break; - } - break; - case 1: - tmp0 = arg1; - arg1 += tmp2; - tmp1 = arg1 < tmp0; - switch (Q) { - case 0: - if (tmp1 == 0) - SETQ; - else - CLRQ; - break; - case 1: - if (tmp1) - SETQ; - else - CLRQ; - break; - } - break; - } - break; - case 1: - switch (M) { - case 0: - tmp0 = arg1; - arg1 += tmp2; - tmp1 = arg1 < tmp0; - switch (Q) { - case 0: - if (tmp1) - SETQ; - else - CLRQ; - break; - case 1: - if (tmp1 == 0) - SETQ; - else - CLRQ; - break; - } - break; - case 1: - tmp0 = arg1; - arg1 -= tmp2; - tmp1 = arg1 > tmp0; - switch (Q) { - case 0: - if (tmp1 == 0) - SETQ; - else - CLRQ; - break; - case 1: - if (tmp1) - SETQ; - else - CLRQ; - break; - } - break; - } - break; - } - if (Q == M) - SETT; - else - CLRT; - //printf("Output: arg1=0x%08x M=%d Q=%d T=%d\n", arg1, M, Q, T); - return arg1; -} - void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1) { int64_t res; @@ -282,7 +164,7 @@ void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1) res += (int64_t) (int32_t) arg0 *(int64_t) (int32_t) arg1; env->mach = (res >> 32) & 0xffffffff; env->macl = res & 0xffffffff; - if (env->sr & SR_S) { + if (env->sr & (1u << SR_S)) { if (res < 0) env->mach |= 0xffff0000; else @@ -298,7 +180,7 @@ void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1) res += (int64_t) (int16_t) arg0 *(int64_t) (int16_t) arg1; env->mach = (res >> 32) & 0xffffffff; env->macl = res & 0xffffffff; - if (env->sr & SR_S) { + if (env->sr & (1u << SR_S)) { if (res < -0x80000000) { env->mach = 1; env->macl = 0x80000000; @@ -309,16 +191,6 @@ void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1) } } -static inline void set_t(CPUSH4State *env) -{ - env->sr |= SR_T; -} - -static inline void clr_t(CPUSH4State *env) -{ - env->sr &= ~SR_T; -} - void helper_ld_fpscr(CPUSH4State *env, uint32_t val) { env->fpscr = val & FPSCR_MASK; @@ -403,10 +275,8 @@ void helper_fcmp_eq_FT(CPUSH4State *env, float32 t0, float32 t1) relation = float32_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(env, GETPC()); - } else if (relation == float_relation_equal) { - set_t(env); } else { - clr_t(env); + env->sr_t = (relation == float_relation_equal); } } @@ -418,10 +288,8 @@ void helper_fcmp_eq_DT(CPUSH4State *env, float64 t0, float64 t1) relation = float64_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(env, GETPC()); - } else if (relation == float_relation_equal) { - set_t(env); } else { - clr_t(env); + env->sr_t = (relation == float_relation_equal); } } @@ -433,10 +301,8 @@ void helper_fcmp_gt_FT(CPUSH4State *env, float32 t0, float32 t1) relation = float32_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(env, GETPC()); - } else if (relation == float_relation_greater) { - set_t(env); } else { - clr_t(env); + env->sr_t = (relation == float_relation_greater); } } @@ -448,10 +314,8 @@ void helper_fcmp_gt_DT(CPUSH4State *env, float64 t0, float64 t1) relation = float64_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(env, GETPC()); - } else if (relation == float_relation_greater) { - set_t(env); } else { - clr_t(env); + env->sr_t = (relation == float_relation_greater); } } diff --git a/target-sh4/translate.c b/target-sh4/translate.c index 41aa928..28259f9 100644 --- a/target-sh4/translate.c +++ b/target-sh4/translate.c @@ -18,7 +18,6 @@ */ #define DEBUG_DISAS -//#define SH4_SINGLE_STEP #include "cpu.h" #include "disas/disas.h" @@ -47,7 +46,7 @@ typedef struct DisasContext { #if defined(CONFIG_USER_ONLY) #define IS_USER(ctx) 1 #else -#define IS_USER(ctx) (!(ctx->flags & SR_MD)) +#define IS_USER(ctx) (!(ctx->flags & (1u << SR_MD))) #endif enum { @@ -62,7 +61,8 @@ enum { /* global register indexes */ static TCGv_ptr cpu_env; static TCGv cpu_gregs[24]; -static TCGv cpu_pc, cpu_sr, cpu_ssr, cpu_spc, cpu_gbr; +static TCGv cpu_sr, cpu_sr_m, cpu_sr_q, cpu_sr_t; +static TCGv cpu_pc, cpu_ssr, cpu_spc, cpu_gbr; static TCGv cpu_vbr, cpu_sgr, cpu_dbr, cpu_mach, cpu_macl; static TCGv cpu_pr, cpu_fpscr, cpu_fpul, cpu_ldst; static TCGv cpu_fregs[32]; @@ -110,6 +110,12 @@ void sh4_translate_init(void) offsetof(CPUSH4State, pc), "PC"); cpu_sr = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSH4State, sr), "SR"); + cpu_sr_m = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUSH4State, sr_m), "SR_M"); + cpu_sr_q = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUSH4State, sr_q), "SR_Q"); + cpu_sr_t = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUSH4State, sr_t), "SR_T"); cpu_ssr = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSH4State, ssr), "SSR"); cpu_spc = tcg_global_mem_new_i32(TCG_AREG0, @@ -156,7 +162,7 @@ void superh_cpu_dump_state(CPUState *cs, FILE *f, CPUSH4State *env = &cpu->env; int i; cpu_fprintf(f, "pc=0x%08x sr=0x%08x pr=0x%08x fpscr=0x%08x\n", - env->pc, env->sr, env->pr, env->fpscr); + env->pc, cpu_read_sr(env), env->pr, env->fpscr); cpu_fprintf(f, "spc=0x%08x ssr=0x%08x gbr=0x%08x vbr=0x%08x\n", env->spc, env->ssr, env->gbr, env->vbr); cpu_fprintf(f, "sgr=0x%08x dbr=0x%08x delayed_pc=0x%08x fpul=0x%08x\n", @@ -175,6 +181,30 @@ void superh_cpu_dump_state(CPUState *cs, FILE *f, } } +static void gen_read_sr(TCGv dst) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_shli_i32(t0, cpu_sr_q, SR_Q); + tcg_gen_or_i32(dst, dst, t0); + tcg_gen_shli_i32(t0, cpu_sr_m, SR_M); + tcg_gen_or_i32(dst, dst, t0); + tcg_gen_shli_i32(t0, cpu_sr_t, SR_T); + tcg_gen_or_i32(dst, cpu_sr, t0); + tcg_temp_free_i32(t0); +} + +static void gen_write_sr(TCGv src) +{ + tcg_gen_andi_i32(cpu_sr, src, + ~((1u << SR_Q) | (1u << SR_M) | (1u << SR_T))); + tcg_gen_shri_i32(cpu_sr_q, src, SR_Q); + tcg_gen_andi_i32(cpu_sr_q, cpu_sr_q, 1); + tcg_gen_shri_i32(cpu_sr_m, src, SR_M); + tcg_gen_andi_i32(cpu_sr_m, cpu_sr_m, 1); + tcg_gen_shri_i32(cpu_sr_t, src, SR_T); + tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); +} + static void gen_goto_tb(DisasContext * ctx, int n, target_ulong dest) { TranslationBlock *tb; @@ -210,12 +240,9 @@ static void gen_jump(DisasContext * ctx) static inline void gen_branch_slot(uint32_t delayed_pc, int t) { - TCGv sr; TCGLabel *label = gen_new_label(); tcg_gen_movi_i32(cpu_delayed_pc, delayed_pc); - sr = tcg_temp_new(); - tcg_gen_andi_i32(sr, cpu_sr, SR_T); - tcg_gen_brcondi_i32(t ? TCG_COND_EQ:TCG_COND_NE, sr, 0, label); + tcg_gen_brcondi_i32(t ? TCG_COND_EQ : TCG_COND_NE, cpu_sr_t, 0, label); tcg_gen_ori_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE); gen_set_label(label); } @@ -224,13 +251,8 @@ static inline void gen_branch_slot(uint32_t delayed_pc, int t) static void gen_conditional_jump(DisasContext * ctx, target_ulong ift, target_ulong ifnott) { - TCGLabel *l1; - TCGv sr; - - l1 = gen_new_label(); - sr = tcg_temp_new(); - tcg_gen_andi_i32(sr, cpu_sr, SR_T); - tcg_gen_brcondi_i32(TCG_COND_NE, sr, 0, l1); + TCGLabel *l1 = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_NE, cpu_sr_t, 0, l1); gen_goto_tb(ctx, 0, ifnott); gen_set_label(l1); gen_goto_tb(ctx, 1, ift); @@ -252,54 +274,12 @@ static void gen_delayed_conditional_jump(DisasContext * ctx) gen_jump(ctx); } -static inline void gen_cmp(int cond, TCGv t0, TCGv t1) -{ - TCGv t; - - t = tcg_temp_new(); - tcg_gen_setcond_i32(cond, t, t1, t0); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t); - - tcg_temp_free(t); -} - -static inline void gen_cmp_imm(int cond, TCGv t0, int32_t imm) -{ - TCGv t; - - t = tcg_temp_new(); - tcg_gen_setcondi_i32(cond, t, t0, imm); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t); - - tcg_temp_free(t); -} - static inline void gen_store_flags(uint32_t flags) { tcg_gen_andi_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE); tcg_gen_ori_i32(cpu_flags, cpu_flags, flags); } -static inline void gen_copy_bit_i32(TCGv t0, int p0, TCGv t1, int p1) -{ - TCGv tmp = tcg_temp_new(); - - p0 &= 0x1f; - p1 &= 0x1f; - - tcg_gen_andi_i32(tmp, t1, (1 << p1)); - tcg_gen_andi_i32(t0, t0, ~(1 << p0)); - if (p0 < p1) - tcg_gen_shri_i32(tmp, tmp, p1 - p0); - else if (p0 > p1) - tcg_gen_shli_i32(tmp, tmp, p0 - p1); - tcg_gen_or_i32(t0, t0, tmp); - - tcg_temp_free(tmp); -} - static inline void gen_load_fpr64(TCGv_i64 t, int reg) { tcg_gen_concat_i32_i64(t, cpu_fregs[reg + 1], cpu_fregs[reg]); @@ -326,10 +306,12 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg) #define B11_8 ((ctx->opcode >> 8) & 0xf) #define B15_12 ((ctx->opcode >> 12) & 0xf) -#define REG(x) ((x) < 8 && (ctx->flags & (SR_MD | SR_RB)) == (SR_MD | SR_RB) \ +#define REG(x) ((x) < 8 && (ctx->flags & (1u << SR_MD))\ + && (ctx->flags & (1u << SR_RB))\ ? (cpu_gregs[x + 16]) : (cpu_gregs[x])) -#define ALTREG(x) ((x) < 8 && (ctx->flags & (SR_MD | SR_RB)) != (SR_MD | SR_RB)\ +#define ALTREG(x) ((x) < 8 && (!(ctx->flags & (1u << SR_MD))\ + || !(ctx->flags & (1u << SR_RB)))\ ? (cpu_gregs[x + 16]) : (cpu_gregs[x])) #define FREG(x) (ctx->flags & FPSCR_FR ? (x) ^ 0x10 : (x)) @@ -359,7 +341,7 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg) } #define CHECK_FPU_ENABLED \ - if (ctx->flags & SR_FD) { \ + if (ctx->flags & (1u << SR_FD)) { \ tcg_gen_movi_i32(cpu_pc, ctx->pc); \ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \ gen_helper_raise_slot_fpu_disable(cpu_env); \ @@ -409,7 +391,9 @@ static void _decode_opc(DisasContext * ctx) switch (ctx->opcode) { case 0x0019: /* div0u */ - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~(SR_M | SR_Q | SR_T)); + tcg_gen_movi_i32(cpu_sr_m, 0); + tcg_gen_movi_i32(cpu_sr_q, 0); + tcg_gen_movi_i32(cpu_sr_t, 0); return; case 0x000b: /* rts */ CHECK_NOT_DELAY_SLOT @@ -422,10 +406,10 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_movi_i32(cpu_macl, 0); return; case 0x0048: /* clrs */ - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_S); + tcg_gen_andi_i32(cpu_sr, cpu_sr, ~(1u << SR_S)); return; case 0x0008: /* clrt */ - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); + tcg_gen_movi_i32(cpu_sr_t, 0); return; case 0x0038: /* ldtlb */ CHECK_PRIVILEGED @@ -434,16 +418,16 @@ static void _decode_opc(DisasContext * ctx) case 0x002b: /* rte */ CHECK_PRIVILEGED CHECK_NOT_DELAY_SLOT - tcg_gen_mov_i32(cpu_sr, cpu_ssr); + gen_write_sr(cpu_ssr); tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc); ctx->flags |= DELAY_SLOT; ctx->delayed_pc = (uint32_t) - 1; return; case 0x0058: /* sets */ - tcg_gen_ori_i32(cpu_sr, cpu_sr, SR_S); + tcg_gen_ori_i32(cpu_sr, cpu_sr, (1u << SR_S)); return; case 0x0018: /* sett */ - tcg_gen_ori_i32(cpu_sr, cpu_sr, SR_T); + tcg_gen_movi_i32(cpu_sr_t, 1); return; case 0xfbfd: /* frchg */ tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_FR); @@ -659,22 +643,14 @@ static void _decode_opc(DisasContext * ctx) return; case 0x300e: /* addc Rm,Rn */ { - TCGv t0, t1, t2; - t0 = tcg_temp_new(); - tcg_gen_andi_i32(t0, cpu_sr, SR_T); + TCGv t0, t1; + t0 = tcg_const_tl(0); t1 = tcg_temp_new(); - tcg_gen_add_i32(t1, REG(B7_4), REG(B11_8)); - tcg_gen_add_i32(t0, t0, t1); - t2 = tcg_temp_new(); - tcg_gen_setcond_i32(TCG_COND_GTU, t2, REG(B11_8), t1); - tcg_gen_setcond_i32(TCG_COND_GTU, t1, t1, t0); - tcg_gen_or_i32(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); - tcg_temp_free(t1); - tcg_gen_mov_i32(REG(B11_8), t0); + tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0); + tcg_gen_add2_i32(REG(B11_8), cpu_sr_t, + REG(B11_8), t0, t1, cpu_sr_t); tcg_temp_free(t0); + tcg_temp_free(t1); } return; case 0x300f: /* addv Rm,Rn */ @@ -686,11 +662,9 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_xor_i32(t1, t0, REG(B11_8)); t2 = tcg_temp_new(); tcg_gen_xor_i32(t2, REG(B7_4), REG(B11_8)); - tcg_gen_andc_i32(t1, t1, t2); + tcg_gen_andc_i32(cpu_sr_t, t1, t2); tcg_temp_free(t2); - tcg_gen_shri_i32(t1, t1, 31); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); + tcg_gen_shri_i32(cpu_sr_t, cpu_sr_t, 31); tcg_temp_free(t1); tcg_gen_mov_i32(REG(B7_4), t0); tcg_temp_free(t0); @@ -700,54 +674,79 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_and_i32(REG(B11_8), REG(B11_8), REG(B7_4)); return; case 0x3000: /* cmp/eq Rm,Rn */ - gen_cmp(TCG_COND_EQ, REG(B7_4), REG(B11_8)); + tcg_gen_setcond_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), REG(B7_4)); return; case 0x3003: /* cmp/ge Rm,Rn */ - gen_cmp(TCG_COND_GE, REG(B7_4), REG(B11_8)); + tcg_gen_setcond_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), REG(B7_4)); return; case 0x3007: /* cmp/gt Rm,Rn */ - gen_cmp(TCG_COND_GT, REG(B7_4), REG(B11_8)); + tcg_gen_setcond_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), REG(B7_4)); return; case 0x3006: /* cmp/hi Rm,Rn */ - gen_cmp(TCG_COND_GTU, REG(B7_4), REG(B11_8)); + tcg_gen_setcond_i32(TCG_COND_GTU, cpu_sr_t, REG(B11_8), REG(B7_4)); return; case 0x3002: /* cmp/hs Rm,Rn */ - gen_cmp(TCG_COND_GEU, REG(B7_4), REG(B11_8)); + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_sr_t, REG(B11_8), REG(B7_4)); return; case 0x200c: /* cmp/str Rm,Rn */ { TCGv cmp1 = tcg_temp_new(); TCGv cmp2 = tcg_temp_new(); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); tcg_gen_xor_i32(cmp1, REG(B7_4), REG(B11_8)); tcg_gen_andi_i32(cmp2, cmp1, 0xff000000); - tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, cmp2, 0); tcg_gen_andi_i32(cmp2, cmp1, 0x00ff0000); tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); + tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); tcg_gen_andi_i32(cmp2, cmp1, 0x0000ff00); tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); + tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); tcg_gen_andi_i32(cmp2, cmp1, 0x000000ff); tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); + tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); tcg_temp_free(cmp2); tcg_temp_free(cmp1); } return; case 0x2007: /* div0s Rm,Rn */ - { - gen_copy_bit_i32(cpu_sr, 8, REG(B11_8), 31); /* SR_Q */ - gen_copy_bit_i32(cpu_sr, 9, REG(B7_4), 31); /* SR_M */ - TCGv val = tcg_temp_new(); - tcg_gen_xor_i32(val, REG(B7_4), REG(B11_8)); - gen_copy_bit_i32(cpu_sr, 0, val, 31); /* SR_T */ - tcg_temp_free(val); - } + tcg_gen_shri_i32(cpu_sr_q, REG(B11_8), 31); /* SR_Q */ + tcg_gen_shri_i32(cpu_sr_m, REG(B7_4), 31); /* SR_M */ + tcg_gen_xor_i32(cpu_sr_t, cpu_sr_q, cpu_sr_m); /* SR_T */ return; case 0x3004: /* div1 Rm,Rn */ - gen_helper_div1(REG(B11_8), cpu_env, REG(B7_4), REG(B11_8)); + { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv zero = tcg_const_i32(0); + + /* shift left arg1, saving the bit being pushed out and inserting + T on the right */ + tcg_gen_shri_i32(t0, REG(B11_8), 31); + tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); + tcg_gen_or_i32(REG(B11_8), REG(B11_8), cpu_sr_t); + + /* Add or subtract arg0 from arg1 depending if Q == M. To avoid + using 64-bit temps, we compute arg0's high part from q ^ m, so + that it is 0x00000000 when adding the value or 0xffffffff when + subtracting it. */ + tcg_gen_xor_i32(t1, cpu_sr_q, cpu_sr_m); + tcg_gen_subi_i32(t1, t1, 1); + tcg_gen_neg_i32(t2, REG(B7_4)); + tcg_gen_movcond_i32(TCG_COND_EQ, t2, t1, zero, REG(B7_4), t2); + tcg_gen_add2_i32(REG(B11_8), t1, REG(B11_8), zero, t2, t1); + + /* compute T and Q depending on carry */ + tcg_gen_andi_i32(t1, t1, 1); + tcg_gen_xor_i32(t1, t1, t0); + tcg_gen_xori_i32(cpu_sr_t, t1, 1); + tcg_gen_xor_i32(cpu_sr_q, cpu_sr_m, t1); + + tcg_temp_free(zero); + tcg_temp_free(t2); + tcg_temp_free(t1); + tcg_temp_free(t0); + } return; case 0x300d: /* dmuls.l Rm,Rn */ tcg_gen_muls2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8)); @@ -827,19 +826,13 @@ static void _decode_opc(DisasContext * ctx) return; case 0x600a: /* negc Rm,Rn */ { - TCGv t0, t1; - t0 = tcg_temp_new(); - tcg_gen_neg_i32(t0, REG(B7_4)); - t1 = tcg_temp_new(); - tcg_gen_andi_i32(t1, cpu_sr, SR_T); - tcg_gen_sub_i32(REG(B11_8), t0, t1); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_setcondi_i32(TCG_COND_GTU, t1, t0, 0); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); - tcg_gen_setcond_i32(TCG_COND_GTU, t1, REG(B11_8), t0); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); + TCGv t0 = tcg_const_i32(0); + tcg_gen_add2_i32(REG(B11_8), cpu_sr_t, + REG(B7_4), t0, cpu_sr_t, t0); + tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t, + t0, t0, REG(B11_8), cpu_sr_t); + tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); tcg_temp_free(t0); - tcg_temp_free(t1); } return; case 0x6007: /* not Rm,Rn */ @@ -918,22 +911,15 @@ static void _decode_opc(DisasContext * ctx) return; case 0x300a: /* subc Rm,Rn */ { - TCGv t0, t1, t2; - t0 = tcg_temp_new(); - tcg_gen_andi_i32(t0, cpu_sr, SR_T); + TCGv t0, t1; + t0 = tcg_const_tl(0); t1 = tcg_temp_new(); - tcg_gen_sub_i32(t1, REG(B11_8), REG(B7_4)); - tcg_gen_sub_i32(t0, t1, t0); - t2 = tcg_temp_new(); - tcg_gen_setcond_i32(TCG_COND_LTU, t2, REG(B11_8), t1); - tcg_gen_setcond_i32(TCG_COND_LTU, t1, t1, t0); - tcg_gen_or_i32(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); - tcg_temp_free(t1); - tcg_gen_mov_i32(REG(B11_8), t0); + tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0); + tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t, + REG(B11_8), t0, t1, cpu_sr_t); + tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); tcg_temp_free(t0); + tcg_temp_free(t1); } return; case 0x300b: /* subv Rm,Rn */ @@ -947,9 +933,7 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_xor_i32(t2, REG(B11_8), REG(B7_4)); tcg_gen_and_i32(t1, t1, t2); tcg_temp_free(t2); - tcg_gen_shri_i32(t1, t1, 31); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); + tcg_gen_shri_i32(cpu_sr_t, t1, 31); tcg_temp_free(t1); tcg_gen_mov_i32(REG(B11_8), t0); tcg_temp_free(t0); @@ -959,7 +943,7 @@ static void _decode_opc(DisasContext * ctx) { TCGv val = tcg_temp_new(); tcg_gen_and_i32(val, REG(B7_4), REG(B11_8)); - gen_cmp_imm(TCG_COND_EQ, val, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); tcg_temp_free(val); } return; @@ -1025,24 +1009,19 @@ static void _decode_opc(DisasContext * ctx) return; case 0xf00b: /* fmov {F,D,X}Rm,@-Rn - FPSCR: Nothing */ CHECK_FPU_ENABLED + TCGv addr = tcg_temp_new_i32(); + tcg_gen_subi_i32(addr, REG(B11_8), 4); if (ctx->flags & FPSCR_SZ) { - TCGv addr = tcg_temp_new_i32(); int fr = XREG(B7_4); - tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st_i32(cpu_fregs[fr+1], addr, ctx->memidx, MO_TEUL); tcg_gen_subi_i32(addr, addr, 4); tcg_gen_qemu_st_i32(cpu_fregs[fr], addr, ctx->memidx, MO_TEUL); - tcg_gen_mov_i32(REG(B11_8), addr); - tcg_temp_free(addr); } else { - TCGv addr; - addr = tcg_temp_new_i32(); - tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], addr, ctx->memidx, MO_TEUL); - tcg_gen_mov_i32(REG(B11_8), addr); - tcg_temp_free(addr); } + tcg_gen_mov_i32(REG(B11_8), addr); + tcg_temp_free(addr); return; case 0xf006: /* fmov @(R0,Rm),{F,D,X}Rm - FPSCR: Nothing */ CHECK_FPU_ENABLED @@ -1210,7 +1189,7 @@ static void _decode_opc(DisasContext * ctx) ctx->flags |= DELAY_SLOT_CONDITIONAL; return; case 0x8800: /* cmp/eq #imm,R0 */ - gen_cmp_imm(TCG_COND_EQ, REG(0), B7_0s); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(0), B7_0s); return; case 0xc400: /* mov.b @(disp,GBR),R0 */ { @@ -1326,7 +1305,7 @@ static void _decode_opc(DisasContext * ctx) { TCGv val = tcg_temp_new(); tcg_gen_andi_i32(val, REG(0), B7_0); - gen_cmp_imm(TCG_COND_EQ, val, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); tcg_temp_free(val); } return; @@ -1336,7 +1315,7 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_add_i32(val, REG(0), cpu_gbr); tcg_gen_qemu_ld_i32(val, val, ctx->memidx, MO_UB); tcg_gen_andi_i32(val, val, B7_0); - gen_cmp_imm(TCG_COND_EQ, val, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); tcg_temp_free(val); } return; @@ -1399,14 +1378,14 @@ static void _decode_opc(DisasContext * ctx) ctx->delayed_pc = (uint32_t) - 1; return; case 0x4015: /* cmp/pl Rn */ - gen_cmp_imm(TCG_COND_GT, REG(B11_8), 0); + tcg_gen_setcondi_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), 0); return; case 0x4011: /* cmp/pz Rn */ - gen_cmp_imm(TCG_COND_GE, REG(B11_8), 0); + tcg_gen_setcondi_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), 0); return; case 0x4010: /* dt Rn */ tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 1); - gen_cmp_imm(TCG_COND_EQ, REG(B11_8), 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), 0); return; case 0x402b: /* jmp @Rn */ CHECK_NOT_DELAY_SLOT @@ -1423,15 +1402,21 @@ static void _decode_opc(DisasContext * ctx) return; case 0x400e: /* ldc Rm,SR */ CHECK_PRIVILEGED - tcg_gen_andi_i32(cpu_sr, REG(B11_8), 0x700083f3); - ctx->bstate = BS_STOP; + { + TCGv val = tcg_temp_new(); + tcg_gen_andi_i32(val, REG(B11_8), 0x700083f3); + gen_write_sr(val); + tcg_temp_free(val); + ctx->bstate = BS_STOP; + } return; case 0x4007: /* ldc.l @Rm+,SR */ CHECK_PRIVILEGED { TCGv val = tcg_temp_new(); tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TESL); - tcg_gen_andi_i32(cpu_sr, val, 0x700083f3); + tcg_gen_andi_i32(val, val, 0x700083f3); + gen_write_sr(val); tcg_temp_free(val); tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); ctx->bstate = BS_STOP; @@ -1439,15 +1424,18 @@ static void _decode_opc(DisasContext * ctx) return; case 0x0002: /* stc SR,Rn */ CHECK_PRIVILEGED - tcg_gen_mov_i32(REG(B11_8), cpu_sr); + gen_read_sr(REG(B11_8)); return; case 0x4003: /* stc SR,@-Rn */ CHECK_PRIVILEGED { TCGv addr = tcg_temp_new(); + TCGv val = tcg_temp_new(); tcg_gen_subi_i32(addr, REG(B11_8), 4); - tcg_gen_qemu_st_i32(cpu_sr, addr, ctx->memidx, MO_TEUL); + gen_read_sr(val); + tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL); tcg_gen_mov_i32(REG(B11_8), addr); + tcg_temp_free(val); tcg_temp_free(addr); } return; @@ -1545,7 +1533,7 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); return; case 0x0029: /* movt Rn */ - tcg_gen_andi_i32(REG(B11_8), cpu_sr, SR_T); + tcg_gen_mov_i32(REG(B11_8), cpu_sr_t); return; case 0x0073: /* MOVCO.L @@ -1555,8 +1543,7 @@ static void _decode_opc(DisasContext * ctx) */ if (ctx->features & SH_FEATURE_SH4A) { TCGLabel *label = gen_new_label(); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, cpu_ldst); + tcg_gen_mov_i32(cpu_sr_t, cpu_ldst); tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ldst, 0, label); tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL); gen_set_label(label); @@ -1609,42 +1596,42 @@ static void _decode_opc(DisasContext * ctx) case 0x4024: /* rotcl Rn */ { TCGv tmp = tcg_temp_new(); - tcg_gen_mov_i32(tmp, cpu_sr); - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 31); + tcg_gen_mov_i32(tmp, cpu_sr_t); + tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31); tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); - gen_copy_bit_i32(REG(B11_8), 0, tmp, 0); + tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp); tcg_temp_free(tmp); } return; case 0x4025: /* rotcr Rn */ { TCGv tmp = tcg_temp_new(); - tcg_gen_mov_i32(tmp, cpu_sr); - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); + tcg_gen_shli_i32(tmp, cpu_sr_t, 31); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); - gen_copy_bit_i32(REG(B11_8), 31, tmp, 0); + tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp); tcg_temp_free(tmp); } return; case 0x4004: /* rotl Rn */ tcg_gen_rotli_i32(REG(B11_8), REG(B11_8), 1); - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0); return; case 0x4005: /* rotr Rn */ - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0); tcg_gen_rotri_i32(REG(B11_8), REG(B11_8), 1); return; case 0x4000: /* shll Rn */ case 0x4020: /* shal Rn */ - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 31); + tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31); tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); return; case 0x4021: /* shar Rn */ - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); tcg_gen_sari_i32(REG(B11_8), REG(B11_8), 1); return; case 0x4001: /* shlr Rn */ - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); return; case 0x4008: /* shll2 Rn */ @@ -1672,7 +1659,7 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_mov_i32(addr, REG(B11_8)); val = tcg_temp_local_new(); tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB); - gen_cmp_imm(TCG_COND_EQ, val, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); tcg_gen_ori_i32(val, val, 0x80); tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB); tcg_temp_free(val); @@ -1874,7 +1861,7 @@ gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, ctx.pc = pc_start; ctx.flags = (uint32_t)tb->flags; ctx.bstate = BS_NONE; - ctx.memidx = (ctx.flags & SR_MD) == 0 ? 1 : 0; + ctx.memidx = (ctx.flags & (1u << SR_MD)) == 0 ? 1 : 0; /* We don't know if the delayed pc came from a dynamic or static branch, so assume it is a dynamic branch. */ ctx.delayed_pc = -1; /* use delayed pc from env pointer */ diff --git a/tests/check-qdict.c b/tests/check-qdict.c index a9296f0..a136f2a 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -152,6 +152,28 @@ static void qdict_get_try_str_test(void) QDECREF(tests_dict); } +static void qdict_defaults_test(void) +{ + QDict *dict, *copy; + + dict = qdict_new(); + copy = qdict_new(); + + qdict_set_default_str(dict, "foo", "abc"); + qdict_set_default_str(dict, "foo", "def"); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc"); + qdict_set_default_str(dict, "bar", "ghi"); + + qdict_copy_default(copy, dict, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc"); + qdict_set_default_str(copy, "bar", "xyz"); + qdict_copy_default(copy, dict, "bar"); + g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); + + QDECREF(copy); + QDECREF(dict); +} + static void qdict_haskey_not_test(void) { QDict *tests_dict = qdict_new(); @@ -444,6 +466,49 @@ static void qdict_array_split_test(void) QDECREF(test_dict); } +static void qdict_array_entries_test(void) +{ + QDict *dict = qdict_new(); + + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); + + qdict_put(dict, "bar", qint_from_int(0)); + qdict_put(dict, "baz.0", qint_from_int(0)); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); + + qdict_put(dict, "foo.1", qint_from_int(0)); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); + qdict_put(dict, "foo.0", qint_from_int(0)); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); + qdict_put(dict, "foo.bar", qint_from_int(0)); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); + qdict_del(dict, "foo.bar"); + + qdict_put(dict, "foo.2.a", qint_from_int(0)); + qdict_put(dict, "foo.2.b", qint_from_int(0)); + qdict_put(dict, "foo.2.c", qint_from_int(0)); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + + QDECREF(dict); + + dict = qdict_new(); + qdict_put(dict, "1", qint_from_int(0)); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + qdict_put(dict, "0", qint_from_int(0)); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); + qdict_put(dict, "bar", qint_from_int(0)); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + qdict_del(dict, "bar"); + + qdict_put(dict, "2.a", qint_from_int(0)); + qdict_put(dict, "2.b", qint_from_int(0)); + qdict_put(dict, "2.c", qint_from_int(0)); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); + + QDECREF(dict); +} + static void qdict_join_test(void) { QDict *dict1, *dict2; @@ -663,6 +728,7 @@ int main(int argc, char **argv) g_test_add_func("/public/get_try_int", qdict_get_try_int_test); g_test_add_func("/public/get_str", qdict_get_str_test); g_test_add_func("/public/get_try_str", qdict_get_try_str_test); + g_test_add_func("/public/defaults", qdict_defaults_test); g_test_add_func("/public/haskey_not", qdict_haskey_not_test); g_test_add_func("/public/haskey", qdict_haskey_test); g_test_add_func("/public/del", qdict_del_test); @@ -670,6 +736,7 @@ int main(int argc, char **argv) g_test_add_func("/public/iterapi", qdict_iterapi_test); g_test_add_func("/public/flatten", qdict_flatten_test); g_test_add_func("/public/array_split", qdict_array_split_test); + g_test_add_func("/public/array_entries", qdict_array_entries_test); g_test_add_func("/public/join", qdict_join_test); g_test_add_func("/errors/put_exists", qdict_put_exists_test); diff --git a/tests/endianness-test.c b/tests/endianness-test.c index 92e17d2..26ee734 100644 --- a/tests/endianness-test.c +++ b/tests/endianness-test.c @@ -31,8 +31,6 @@ struct TestCase { static const TestCase test_cases[] = { { "i386", "pc", -1 }, - { "mips", "magnum", 0x90000000, .bswap = true }, - { "mips", "pica61", 0x90000000, .bswap = true }, { "mips", "mips", 0x14000000, .bswap = true }, { "mips", "malta", 0x10000000, .bswap = true }, { "mips64", "magnum", 0x90000000, .bswap = true }, diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 0360f37..4a8055b 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -194,7 +194,6 @@ echo === Specifying the protocol layer === echo run_qemu -drive file="$TEST_IMG",file.driver=file -run_qemu -drive file="$TEST_IMG",file.driver=qcow2 echo echo === Leaving out required options === diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index 2890eac..652dd63 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -253,9 +253,6 @@ Testing: -drive file=TEST_DIR/t.qcow2,file.driver=file QEMU X.Y.Z monitor - type 'help' for more information (qemu) q[K[Dqu[K[D[Dqui[K[D[D[Dquit[K -Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2 -QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename' - === Leaving out required options === diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 index b9096a5..c0e9e2b 100755 --- a/tests/qemu-iotests/093 +++ b/tests/qemu-iotests/093 @@ -3,6 +3,7 @@ # Tests for IO throttling # # Copyright (C) 2015 Red Hat, Inc. +# Copyright (C) 2015 Igalia, S.L. # # 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 @@ -22,6 +23,7 @@ import iotests class ThrottleTestCase(iotests.QMPTestCase): test_img = "null-aio://" + max_drives = 3 def blockstats(self, device): result = self.vm.qmp("query-blockstats") @@ -32,26 +34,31 @@ class ThrottleTestCase(iotests.QMPTestCase): raise Exception("Device not found for blockstats: %s" % device) def setUp(self): - self.vm = iotests.VM().add_drive(self.test_img) + self.vm = iotests.VM() + for i in range(0, self.max_drives): + self.vm.add_drive(self.test_img) self.vm.launch() def tearDown(self): self.vm.shutdown() - def do_test_throttle(self, seconds, params): + def do_test_throttle(self, ndrives, seconds, params): def check_limit(limit, num): # IO throttling algorithm is discrete, allow 10% error so the test # is more robust return limit == 0 or \ - (num < seconds * limit * 1.1 - and num > seconds * limit * 0.9) + (num < seconds * limit * 1.1 / ndrives + and num > seconds * limit * 0.9 / ndrives) nsec_per_sec = 1000000000 - params['device'] = 'drive0' + params['group'] = 'test' - result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) - self.assert_qmp(result, 'return', {}) + # Set the I/O throttling parameters to all drives + for i in range(0, ndrives): + params['device'] = 'drive%d' % i + result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) + self.assert_qmp(result, 'return', {}) # Set vm clock to a known value ns = seconds * nsec_per_sec @@ -66,32 +73,60 @@ class ThrottleTestCase(iotests.QMPTestCase): params['iops'] / 2, params['iops_rd']) rd_nr *= seconds * 2 + rd_nr /= ndrives wr_nr = max(params['bps'] / rq_size / 2, params['bps_wr'] / rq_size, params['iops'] / 2, params['iops_wr']) wr_nr *= seconds * 2 + wr_nr /= ndrives + + # Send I/O requests to all drives for i in range(rd_nr): - self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % (i * rq_size, rq_size)) - for i in range(wr_nr): - self.vm.hmp_qemu_io("drive0", "aio_write %d %d" % (i * rq_size, rq_size)) + for drive in range(0, ndrives): + self.vm.hmp_qemu_io("drive%d" % drive, "aio_read %d %d" % + (i * rq_size, rq_size)) - start_rd_bytes, start_rd_iops, start_wr_bytes, start_wr_iops = self.blockstats('drive0') + for i in range(wr_nr): + for drive in range(0, ndrives): + self.vm.hmp_qemu_io("drive%d" % drive, "aio_write %d %d" % + (i * rq_size, rq_size)) + + # We'll store the I/O stats for each drive in these arrays + start_rd_bytes = [0] * ndrives + start_rd_iops = [0] * ndrives + start_wr_bytes = [0] * ndrives + start_wr_iops = [0] * ndrives + end_rd_bytes = [0] * ndrives + end_rd_iops = [0] * ndrives + end_wr_bytes = [0] * ndrives + end_wr_iops = [0] * ndrives + + # Read the stats before advancing the clock + for i in range(0, ndrives): + start_rd_bytes[i], start_rd_iops[i], start_wr_bytes[i], \ + start_wr_iops[i] = self.blockstats('drive%d' % i) self.vm.qtest("clock_step %d" % ns) - end_rd_bytes, end_rd_iops, end_wr_bytes, end_wr_iops = self.blockstats('drive0') - - rd_bytes = end_rd_bytes - start_rd_bytes - rd_iops = end_rd_iops - start_rd_iops - wr_bytes = end_wr_bytes - start_wr_bytes - wr_iops = end_wr_iops - start_wr_iops - self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes)) - self.assertTrue(check_limit(params['bps_rd'], rd_bytes)) - self.assertTrue(check_limit(params['bps_wr'], wr_bytes)) - self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops)) - self.assertTrue(check_limit(params['iops_rd'], rd_iops)) - self.assertTrue(check_limit(params['iops_wr'], wr_iops)) + # Read the stats after advancing the clock + for i in range(0, ndrives): + end_rd_bytes[i], end_rd_iops[i], end_wr_bytes[i], \ + end_wr_iops[i] = self.blockstats('drive%d' % i) + + # Check that the I/O is within the limits and evenly distributed + for i in range(0, ndrives): + rd_bytes = end_rd_bytes[i] - start_rd_bytes[i] + rd_iops = end_rd_iops[i] - start_rd_iops[i] + wr_bytes = end_wr_bytes[i] - start_wr_bytes[i] + wr_iops = end_wr_iops[i] - start_wr_iops[i] + + self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes)) + self.assertTrue(check_limit(params['bps_rd'], rd_bytes)) + self.assertTrue(check_limit(params['bps_wr'], wr_bytes)) + self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops)) + self.assertTrue(check_limit(params['iops_rd'], rd_iops)) + self.assertTrue(check_limit(params['iops_wr'], wr_iops)) def test_all(self): params = {"bps": 4096, @@ -101,11 +136,13 @@ class ThrottleTestCase(iotests.QMPTestCase): "iops_rd": 10, "iops_wr": 10, } - # Pick each out of all possible params and test - for tk in params: - limits = dict([(k, 0) for k in params]) - limits[tk] = params[tk] - self.do_test_throttle(5, limits) + # Repeat the test with different numbers of drives + for ndrives in range(1, self.max_drives + 1): + # Pick each out of all possible params and test + for tk in params: + limits = dict([(k, 0) for k in params]) + limits[tk] = params[tk] * ndrives + self.do_test_throttle(ndrives, 5, limits) class ThrottleTestCoroutine(ThrottleTestCase): test_img = "null-co://" diff --git a/tests/qemu-iotests/103 b/tests/qemu-iotests/103 index ccab551..fa9a3c1 100755 --- a/tests/qemu-iotests/103 +++ b/tests/qemu-iotests/103 @@ -93,6 +93,16 @@ $QEMU_IO -c "open -o l2-cache-size=1M,refcount-cache-size=0.25M $TEST_IMG" \ -c 'read -P 42 0 64k' \ | _filter_qemu_io +echo +echo '=== Testing minimal L2 cache and COW ===' +echo + +$QEMU_IMG snapshot -c foo "$TEST_IMG" +# This requires a COW operation, which accesses two L2 tables simultaneously +# (COW source and destination), so there must be enough space in the cache to +# place both tables there (and qemu should not crash) +$QEMU_IO -c "open -o cache-size=0 $TEST_IMG" -c 'write 0 64k' | _filter_qemu_io + # success, all done echo '*** done' rm -f $seq.full diff --git a/tests/qemu-iotests/103.out b/tests/qemu-iotests/103.out index ee705b0..d05f49f 100644 --- a/tests/qemu-iotests/103.out +++ b/tests/qemu-iotests/103.out @@ -26,4 +26,9 @@ read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing minimal L2 cache and COW === + +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done diff --git a/tests/qemu-iotests/119 b/tests/qemu-iotests/119 new file mode 100755 index 0000000..9a11f1b --- /dev/null +++ b/tests/qemu-iotests/119 @@ -0,0 +1,60 @@ +#!/bin/bash +# +# NBD test case for overriding BDRV_O_PROTOCOL by explicitly specifying +# a driver +# +# Copyright (C) 2015 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto nbd +_supported_os Linux + +_make_test_img 64M +# This should not crash +echo "{'execute': 'qmp_capabilities'} + {'execute': 'human-monitor-command', + 'arguments': {'command-line': 'qemu-io drv \"read -P 0 0 64k\"'}} + {'execute': 'quit'}" \ + | $QEMU -drive id=drv,if=none,file="$TEST_IMG",driver=nbd \ + -qmp stdio -nodefaults \ + | _filter_qmp | _filter_qemu_io + +# success, all done +echo +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/119.out b/tests/qemu-iotests/119.out new file mode 100644 index 0000000..58e7114 --- /dev/null +++ b/tests/qemu-iotests/119.out @@ -0,0 +1,11 @@ +QA output created by 119 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +QMP_VERSION +{"return": {}} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": ""} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} + +*** done diff --git a/tests/qemu-iotests/120 b/tests/qemu-iotests/120 new file mode 100755 index 0000000..9f13078 --- /dev/null +++ b/tests/qemu-iotests/120 @@ -0,0 +1,65 @@ +#!/bin/bash +# +# Non-NBD test cases for overriding BDRV_O_PROTOCOL by explicitly +# specifying a driver +# +# Copyright (C) 2015 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=mreitz@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt generic +_supported_proto file +_supported_os Linux + +_make_test_img 64M + +echo "{'execute': 'qmp_capabilities'} + {'execute': 'human-monitor-command', + 'arguments': {'command-line': 'qemu-io drv \"write -P 42 0 64k\"'}} + {'execute': 'quit'}" \ + | $QEMU -qmp stdio -nodefaults \ + -drive id=drv,if=none,file="$TEST_IMG",driver=raw,file.driver=$IMGFMT \ + | _filter_qmp | _filter_qemu_io +$QEMU_IO -c 'read -P 42 0 64k' "$TEST_IMG" | _filter_qemu_io + +$QEMU_IO_PROG -c 'read -P 42 0 64k' \ + "json:{'driver': 'raw', 'file': {'driver': '$IMGFMT', 'file': {'filename': '$TEST_IMG'}}}" \ + | _filter_qemu_io + +# success, all done +echo +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/120.out b/tests/qemu-iotests/120.out new file mode 100644 index 0000000..9131b1b --- /dev/null +++ b/tests/qemu-iotests/120.out @@ -0,0 +1,15 @@ +QA output created by 120 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +QMP_VERSION +{"return": {}} +wrote 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": ""} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +*** done diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index 3ee78cd..8abce2f 100644 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 @@ -125,7 +125,7 @@ class TestIncrementalBackup(iotests.QMPTestCase): event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED", match={'data': {'device': kwargs['device']}}) - self.assertIsNotNone(event) + self.assertNotEqual(event, None) try: failure = self.dictpath(event, 'data/error') diff --git a/tests/qemu-iotests/128 b/tests/qemu-iotests/128 index 249a865..e2a0f2f 100755 --- a/tests/qemu-iotests/128 +++ b/tests/qemu-iotests/128 @@ -29,6 +29,7 @@ tmp=/tmp/$$ status=1 # failure is the default! devname="eiodev$$" +sudo="" _setup_eiodev() { @@ -37,6 +38,7 @@ _setup_eiodev() echo "0 $((1024 * 1024 * 1024 / 512)) error" | \ $cmd dmsetup create "$devname" 2>/dev/null if [ "$?" -eq 0 ]; then + sudo="$cmd" return fi done @@ -74,7 +76,7 @@ TEST_IMG="/dev/mapper/$devname" echo echo "== reading from error device ==" # Opening image should succeed but the read operation should fail -$QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io +$sudo $QEMU_IO --format "$IMGFMT" --nocache -c "read 0 65536" "$TEST_IMG" | _filter_qemu_io # success, all done echo "*** done" diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 0b817ca..4597fc1 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -121,6 +121,8 @@ 114 rw auto quick 115 rw auto 116 rw auto quick +119 rw auto quick +120 rw auto quick 121 rw auto 122 rw auto 123 rw auto quick diff --git a/tests/rocker/bridge b/tests/rocker/bridge index 7a03f9a..46abc6f 100755 --- a/tests/rocker/bridge +++ b/tests/rocker/bridge @@ -9,8 +9,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done # configure a 2-port bridge simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0" # turn off vlan default_pvid on br0 @@ -18,28 +18,23 @@ simp ssh tut sw1 --cmd "echo 0 | sudo dd of=/sys/class/net/br0/bridge/default_pv # turn off learning and flooding in SW -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off" - -# turn on learning in HW - -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off" # bring up bridge and ports simp ssh tut sw1 --cmd "sudo ifconfig br0 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp1 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp2 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up" simp ssh tut sw1 --cmd "sudo ifconfig br0 11.0.0.3/24" # config IP on hosts -simp ssh tut h1 --cmd "sudo ifconfig swp1 11.0.0.1/24" -simp ssh tut h2 --cmd "sudo ifconfig swp1 11.0.0.2/24" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1 11.0.0.2/24" # test... diff --git a/tests/rocker/bridge-stp b/tests/rocker/bridge-stp index 4a111a1..008568a 100755 --- a/tests/rocker/bridge-stp +++ b/tests/rocker/bridge-stp @@ -10,8 +10,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge" simp ssh tut sw1 --cmd "sudo brctl stp br0 on" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0" # turn off vlan default_pvid on br0 @@ -19,27 +19,22 @@ simp ssh tut sw1 --cmd "echo 0 | sudo dd of=/sys/class/net/br0/bridge/default_pv # turn off learning and flooding in SW -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off" - -# turn on learning in HW - -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off" # config IP on hosts -simp ssh tut h1 --cmd "sudo ifconfig swp1 11.0.0.1/24" -simp ssh tut h2 --cmd "sudo ifconfig swp1 11.0.0.2/24" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1 11.0.0.2/24" # bring up bridge and ports simp ssh tut sw1 --cmd "sudo ifconfig br0 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp1 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp2 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up" # test... diff --git a/tests/rocker/bridge-vlan b/tests/rocker/bridge-vlan index 9fa3431..ef9e5f5 100755 --- a/tests/rocker/bridge-vlan +++ b/tests/rocker/bridge-vlan @@ -9,8 +9,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done # configure a 2-port bridge simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0" # turn off vlan default_pvid on br0 # turn on vlan filtering on br0 @@ -20,37 +20,32 @@ simp ssh tut sw1 --cmd "echo 1 | sudo dd of=/sys/class/net/br0/bridge/vlan_filte # add both ports to VLAN 57 -simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp1 master" -simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp2 master" +simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p1 master self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p2 master self" # turn off learning and flooding in SW -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off" - -# turn on learning in HW - -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off" # bring up bridge and ports simp ssh tut sw1 --cmd "sudo ifconfig br0 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp1 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp2 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up" # config IP on host VLANs -simp ssh tut h1 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1" -simp ssh tut h1 --cmd "sudo ifconfig swp1 up" -simp ssh tut h1 --cmd "sudo ifconfig swp1.57 11.0.0.1/24" +simp ssh tut h1 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1.57 11.0.0.1/24" -simp ssh tut h2 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1" -simp ssh tut h2 --cmd "sudo ifconfig swp1 up" -simp ssh tut h2 --cmd "sudo ifconfig swp1.57 11.0.0.2/24" +simp ssh tut h2 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1.57 11.0.0.2/24" # test... diff --git a/tests/rocker/bridge-vlan-stp b/tests/rocker/bridge-vlan-stp index 77ab67e..c660312 100755 --- a/tests/rocker/bridge-vlan-stp +++ b/tests/rocker/bridge-vlan-stp @@ -10,8 +10,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge" simp ssh tut sw1 --cmd "sudo brctl stp br0 on" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0" # turn off vlan default_pvid on br0 # turn on vlan filtering on br0 @@ -21,37 +21,32 @@ simp ssh tut sw1 --cmd "echo 1 | sudo dd of=/sys/class/net/br0/bridge/vlan_filte # add both ports to VLAN 57 -simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp1 master" -simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp2 master" +simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p1 master self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p2 master self" # turn off learning and flooding in SW -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off" - -# turn on learning in HW - -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off" # config IP on host VLANs -simp ssh tut h1 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1" -simp ssh tut h1 --cmd "sudo ifconfig swp1 up" -simp ssh tut h1 --cmd "sudo ifconfig swp1.57 11.0.0.1/24" +simp ssh tut h1 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1.57 11.0.0.1/24" -simp ssh tut h2 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1" -simp ssh tut h2 --cmd "sudo ifconfig swp1 up" -simp ssh tut h2 --cmd "sudo ifconfig swp1.57 11.0.0.2/24" +simp ssh tut h2 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1.57 11.0.0.2/24" # bring up bridge and ports simp ssh tut sw1 --cmd "sudo ifconfig br0 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp1 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp2 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up" # test... diff --git a/tests/rocker/port b/tests/rocker/port index 3437f7d..5f2c248 100755 --- a/tests/rocker/port +++ b/tests/rocker/port @@ -7,13 +7,13 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done # bring up DUT ports -simp ssh tut sw1 --cmd "sudo ifconfig swp1 11.0.0.1/24" -simp ssh tut sw1 --cmd "sudo ifconfig swp2 12.0.0.1/24" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 12.0.0.1/24" # config IP on hosts -simp ssh tut h1 --cmd "sudo ifconfig swp1 11.0.0.2/24" -simp ssh tut h2 --cmd "sudo ifconfig swp1 12.0.0.2/24" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.2/24" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1 12.0.0.2/24" # test... diff --git a/tests/test-aio.c b/tests/test-aio.c index 4b0cb45..a7cb5c9 100644 --- a/tests/test-aio.c +++ b/tests/test-aio.c @@ -107,7 +107,6 @@ static void test_notify(void) typedef struct { QemuMutex start_lock; - EventNotifier notifier; bool thread_acquired; } AcquireTestData; @@ -119,8 +118,6 @@ static void *test_acquire_thread(void *opaque) qemu_mutex_lock(&data->start_lock); qemu_mutex_unlock(&data->start_lock); - g_usleep(500000); - event_notifier_set(&data->notifier); aio_context_acquire(ctx); aio_context_release(ctx); @@ -129,19 +126,20 @@ static void *test_acquire_thread(void *opaque) return NULL; } -static void dummy_notifier_read(EventNotifier *n) +static void dummy_notifier_read(EventNotifier *unused) { - event_notifier_test_and_clear(n); + g_assert(false); /* should never be invoked */ } static void test_acquire(void) { QemuThread thread; + EventNotifier notifier; AcquireTestData data; /* Dummy event notifier ensures aio_poll() will block */ - event_notifier_init(&data.notifier, false); - aio_set_event_notifier(ctx, &data.notifier, dummy_notifier_read); + event_notifier_init(¬ifier, false); + aio_set_event_notifier(ctx, ¬ifier, dummy_notifier_read); g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */ qemu_mutex_init(&data.start_lock); @@ -155,13 +153,12 @@ static void test_acquire(void) /* Block in aio_poll(), let other thread kick us and acquire context */ aio_context_acquire(ctx); qemu_mutex_unlock(&data.start_lock); /* let the thread run */ - g_assert(aio_poll(ctx, true)); - g_assert(!data.thread_acquired); + g_assert(!aio_poll(ctx, true)); aio_context_release(ctx); qemu_thread_join(&thread); - aio_set_event_notifier(ctx, &data.notifier, NULL); - event_notifier_cleanup(&data.notifier); + aio_set_event_notifier(ctx, ¬ifier, NULL); + event_notifier_cleanup(¬ifier); g_assert(data.thread_acquired); } diff --git a/tests/test-throttle.c b/tests/test-throttle.c index d8ba415..0168445 100644 --- a/tests/test-throttle.c +++ b/tests/test-throttle.c @@ -1,10 +1,12 @@ /* * Throttle infrastructure tests * - * Copyright Nodalink, SARL. 2013 + * Copyright Nodalink, EURL. 2013-2014 + * Copyright Igalia, S.L. 2015 * * Authors: - * Benoît Canet <benoit.canet@irqsave.net> + * Benoît Canet <benoit.canet@nodalink.com> + * Alberto Garcia <berto@igalia.com> * * This work is licensed under the terms of the GNU LGPL, version 2 or later. * See the COPYING.LIB file in the top-level directory. @@ -15,11 +17,13 @@ #include "block/aio.h" #include "qemu/throttle.h" #include "qemu/error-report.h" +#include "block/throttle-groups.h" static AioContext *ctx; static LeakyBucket bkt; static ThrottleConfig cfg; static ThrottleState ts; +static ThrottleTimers tt; /* useful function */ static bool double_cmp(double x, double y) @@ -103,17 +107,19 @@ static void test_init(void) { int i; - /* fill the structure with crap */ + /* fill the structures with crap */ memset(&ts, 1, sizeof(ts)); + memset(&tt, 1, sizeof(tt)); - /* init the structure */ - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); + /* init structures */ + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); /* check initialized fields */ - g_assert(ts.clock_type == QEMU_CLOCK_VIRTUAL); - g_assert(ts.timers[0]); - g_assert(ts.timers[1]); + g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL); + g_assert(tt.timers[0]); + g_assert(tt.timers[1]); /* check other fields where cleared */ g_assert(!ts.previous_leak); @@ -124,17 +130,18 @@ static void test_init(void) g_assert(!ts.cfg.buckets[i].level); } - throttle_destroy(&ts); + throttle_timers_destroy(&tt); } static void test_destroy(void) { int i; - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); - throttle_destroy(&ts); + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); + throttle_timers_destroy(&tt); for (i = 0; i < 2; i++) { - g_assert(!ts.timers[i]); + g_assert(!tt.timers[i]); } } @@ -170,11 +177,12 @@ static void test_config_functions(void) orig_cfg.op_size = 1; - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); /* structure reset by throttle_init previous_leak should be null */ g_assert(!ts.previous_leak); - throttle_config(&ts, &orig_cfg); + throttle_config(&ts, &tt, &orig_cfg); /* has previous leak been initialized by throttle_config ? */ g_assert(ts.previous_leak); @@ -182,7 +190,7 @@ static void test_config_functions(void) /* get back the fixed configuration */ throttle_get_config(&ts, &final_cfg); - throttle_destroy(&ts); + throttle_timers_destroy(&tt); g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153); g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56); @@ -323,43 +331,47 @@ static void test_is_valid(void) static void test_have_timer(void) { - /* zero the structure */ + /* zero structures */ memset(&ts, 0, sizeof(ts)); + memset(&tt, 0, sizeof(tt)); /* no timer set should return false */ - g_assert(!throttle_have_timer(&ts)); + g_assert(!throttle_timers_are_initialized(&tt)); - /* init the structure */ - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); + /* init structures */ + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); /* timer set by init should return true */ - g_assert(throttle_have_timer(&ts)); + g_assert(throttle_timers_are_initialized(&tt)); - throttle_destroy(&ts); + throttle_timers_destroy(&tt); } static void test_detach_attach(void) { - /* zero the structure */ + /* zero structures */ memset(&ts, 0, sizeof(ts)); + memset(&tt, 0, sizeof(tt)); /* init the structure */ - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); /* timer set by init should return true */ - g_assert(throttle_have_timer(&ts)); + g_assert(throttle_timers_are_initialized(&tt)); /* timer should no longer exist after detaching */ - throttle_detach_aio_context(&ts); - g_assert(!throttle_have_timer(&ts)); + throttle_timers_detach_aio_context(&tt); + g_assert(!throttle_timers_are_initialized(&tt)); /* timer should exist again after attaching */ - throttle_attach_aio_context(&ts, ctx); - g_assert(throttle_have_timer(&ts)); + throttle_timers_attach_aio_context(&tt, ctx); + g_assert(throttle_timers_are_initialized(&tt)); - throttle_destroy(&ts); + throttle_timers_destroy(&tt); } static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ @@ -387,9 +399,10 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ cfg.op_size = op_size; - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); - throttle_config(&ts, &cfg); + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); + throttle_config(&ts, &tt, &cfg); /* account a read */ throttle_account(&ts, false, size); @@ -414,7 +427,7 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ return false; } - throttle_destroy(&ts); + throttle_timers_destroy(&tt); return true; } @@ -490,23 +503,80 @@ static void test_accounting(void) (64.0 / 13))); } +static void test_groups(void) +{ + ThrottleConfig cfg1, cfg2; + BlockDriverState *bdrv1, *bdrv2, *bdrv3; + + bdrv1 = bdrv_new(); + bdrv2 = bdrv_new(); + bdrv3 = bdrv_new(); + + g_assert(bdrv1->throttle_state == NULL); + g_assert(bdrv2->throttle_state == NULL); + g_assert(bdrv3->throttle_state == NULL); + + throttle_group_register_bs(bdrv1, "bar"); + throttle_group_register_bs(bdrv2, "foo"); + throttle_group_register_bs(bdrv3, "bar"); + + g_assert(bdrv1->throttle_state != NULL); + g_assert(bdrv2->throttle_state != NULL); + g_assert(bdrv3->throttle_state != NULL); + + g_assert(!strcmp(throttle_group_get_name(bdrv1), "bar")); + g_assert(!strcmp(throttle_group_get_name(bdrv2), "foo")); + g_assert(bdrv1->throttle_state == bdrv3->throttle_state); + + /* Setting the config of a group member affects the whole group */ + memset(&cfg1, 0, sizeof(cfg1)); + cfg1.buckets[THROTTLE_BPS_READ].avg = 500000; + cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000; + cfg1.buckets[THROTTLE_OPS_READ].avg = 20000; + cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000; + throttle_group_config(bdrv1, &cfg1); + + throttle_group_get_config(bdrv1, &cfg1); + throttle_group_get_config(bdrv3, &cfg2); + g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); + + cfg2.buckets[THROTTLE_BPS_READ].avg = 4547; + cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349; + cfg2.buckets[THROTTLE_OPS_READ].avg = 123; + cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86; + throttle_group_config(bdrv3, &cfg1); + + throttle_group_get_config(bdrv1, &cfg1); + throttle_group_get_config(bdrv3, &cfg2); + g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); + + throttle_group_unregister_bs(bdrv1); + throttle_group_unregister_bs(bdrv2); + throttle_group_unregister_bs(bdrv3); + + g_assert(bdrv1->throttle_state == NULL); + g_assert(bdrv2->throttle_state == NULL); + g_assert(bdrv3->throttle_state == NULL); +} + int main(int argc, char **argv) { - GSource *src; Error *local_error = NULL; - init_clocks(); + qemu_init_main_loop(&local_error); + ctx = qemu_get_aio_context(); - ctx = aio_context_new(&local_error); if (!ctx) { error_report("Failed to create AIO Context: '%s'", - error_get_pretty(local_error)); - error_free(local_error); + local_error ? error_get_pretty(local_error) : + "Failed to initialize the QEMU main loop"); + if (local_error) { + error_free(local_error); + } exit(1); } - src = aio_get_g_source(ctx); - g_source_attach(src, NULL); - g_source_unref(src); + + bdrv_init(); do {} while (g_main_context_iteration(NULL, false)); @@ -523,6 +593,7 @@ int main(int argc, char **argv) g_test_add_func("/throttle/config/is_valid", test_is_valid); g_test_add_func("/throttle/config_functions", test_config_functions); g_test_add_func("/throttle/accounting", test_accounting); + g_test_add_func("/throttle/groups", test_groups); return g_test_run(); } @@ -25,10 +25,8 @@ //#define DEBUG -#define MAX_STRUCTS 128 - -/* XXX: make it dynamic */ -StructEntry struct_entries[MAX_STRUCTS]; +static unsigned int max_struct_entries; +StructEntry *struct_entries; static const argtype *thunk_type_next_ptr(const argtype *type_ptr); @@ -70,6 +68,7 @@ void thunk_register_struct(int id, const char *name, const argtype *types) StructEntry *se; int nb_fields, offset, max_align, align, size, i, j; + assert(id < max_struct_entries); se = struct_entries + id; /* first we count the number of fields */ @@ -117,6 +116,8 @@ void thunk_register_struct_direct(int id, const char *name, const StructEntry *se1) { StructEntry *se; + + assert(id < max_struct_entries); se = struct_entries + id; *se = *se1; se->name = name; @@ -244,6 +245,7 @@ const argtype *thunk_convert(void *dst, const void *src, const argtype *field_types; const int *dst_offsets, *src_offsets; + assert(*type_ptr < max_struct_entries); se = struct_entries + *type_ptr++; if (se->convert[0] != NULL) { /* specific conversion is needed */ @@ -314,3 +316,9 @@ int thunk_type_align_array(const argtype *type_ptr, int is_host) return thunk_type_align(type_ptr, is_host); } #endif /* ndef NO_THUNK_TYPE_SIZE */ + +void thunk_init(unsigned int max_structs) +{ + max_struct_entries = max_structs; + struct_entries = g_new0(StructEntry, max_structs); +} diff --git a/trace-events b/trace-events index 2662ffa..52b7efa 100644 --- a/trace-events +++ b/trace-events @@ -280,6 +280,12 @@ slavio_timer_mem_writel_mode_counter(unsigned int timer_index) "processor %d cha slavio_timer_mem_writel_mode_invalid(void) "not system timer" slavio_timer_mem_writel_invalid(uint64_t addr) "invalid write address %"PRIx64 +# hw/dma/rc4030.c +jazzio_read(uint64_t addr, uint32_t ret) "read reg[0x%"PRIx64"] = 0x%x" +jazzio_write(uint64_t addr, uint32_t val) "write reg[0x%"PRIx64"] = 0x%x" +rc4030_read(uint64_t addr, uint32_t ret) "read reg[0x%"PRIx64"] = 0x%x" +rc4030_write(uint64_t addr, uint32_t val) "write reg[0x%"PRIx64"] = 0x%x" + # hw/dma/sparc32_dma.c ledma_memory_read(uint64_t addr) "DMA read addr 0x%"PRIx64 ledma_memory_write(uint64_t addr) "DMA write addr 0x%"PRIx64 @@ -1179,13 +1185,14 @@ virtio_gpu_cmd_res_flush(uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint3 virtio_gpu_fence_ctrl(uint64_t fence, uint32_t type) "fence 0x%" PRIx64 ", type 0x%x" virtio_gpu_fence_resp(uint64_t fence) "fence 0x%" PRIx64 -# savevm.c +# migration/savevm.c qemu_loadvm_state_section(unsigned int section_type) "%d" qemu_loadvm_state_section_partend(uint32_t section_id) "%u" qemu_loadvm_state_section_startfull(uint32_t section_id, const char *idstr, uint32_t instance_id, uint32_t version_id) "%u(%s) %u %u" savevm_section_start(const char *id, unsigned int section_id) "%s, section_id %u" savevm_section_end(const char *id, unsigned int section_id, int ret) "%s, section_id %u -> %d" savevm_state_begin(void) "" +savevm_state_header(void) "" savevm_state_iterate(void) "" savevm_state_complete(void) "" savevm_state_cancel(void) "" @@ -1205,7 +1212,7 @@ vmstate_subsection_load_good(const char *parent) "%s" # qemu-file.c qemu_file_fclose(void) "" -# arch_init.c +# migration/ram.c migration_bitmap_sync_start(void) "" migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64"" migration_throttle(void) "" @@ -1625,3 +1632,19 @@ cpu_unhalt(int cpu_index) "unhalting cpu %d" # hw/arm/virt-acpi-build.c virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." + +# audio/alsaaudio.c +alsa_revents(int revents) "revents = %d" +alsa_pollout(int i, int fd) "i = %d fd = %d" +alsa_set_handler(int events, int index, int fd, int err) "events=%#x index=%d fd=%d err=%d" +alsa_wrote_zero(int len) "Failed to write %d frames (wrote zero)" +alsa_read_zero(long len) "Failed to read %ld frames (read zero)" +alsa_xrun_out(void) "Recovering from playback xrun" +alsa_xrun_in(void) "Recovering from capture xrun" +alsa_resume_out(void) "Resuming suspended output stream" +alsa_resume_in(void) "Resuming suspended input stream" +alsa_no_frames(int state) "No frames available and ALSA state is %d" + +# audio/ossaudio.c +oss_version(int version) "OSS version = %#x" +oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 2ddd259..62a5fc4 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -86,7 +86,7 @@ long vnc_client_write_sasl(VncState *vs) * SASL encoded output */ if (vs->output.offset == 0) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); } return ret; diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index 03ea48a..8fc965b 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -94,7 +94,7 @@ static int vnc_start_vencrypt_handshake(VncState *vs) } VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs); start_auth_vencrypt_subauth(vs); diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index 38a1b8b..8c18268 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -56,7 +56,7 @@ static int vncws_start_tls_handshake(VncState *vs) } VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); return 0; } @@ -98,7 +98,7 @@ void vncws_handshake_read(void *opaque) handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer, vs->ws_input.offset, WS_HANDSHAKE_END); if (handshake_end) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset); buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer + strlen(WS_HANDSHAKE_END)); @@ -176,7 +176,7 @@ long vnc_client_write_ws(VncState *vs) buffer_advance(&vs->ws_output, ret); if (vs->ws_output.offset == 0) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); } return ret; @@ -1213,7 +1213,7 @@ static void vnc_disconnect_start(VncState *vs) if (vs->csock == -1) return; vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED); - qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(vs->csock, NULL, NULL, NULL); closesocket(vs->csock); vs->csock = -1; } @@ -1387,7 +1387,7 @@ static long vnc_client_write_plain(VncState *vs) buffer_advance(&vs->output, ret); if (vs->output.offset == 0) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); } return ret; @@ -1434,7 +1434,7 @@ void vnc_client_write(void *opaque) ) { vnc_client_write_locked(opaque); } else if (vs->csock != -1) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); } vnc_unlock_output(vs); } @@ -1581,7 +1581,7 @@ void vnc_write(VncState *vs, const void *data, size_t len) buffer_reserve(&vs->output, len); if (vs->csock != -1 && buffer_empty(&vs->output)) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs); } buffer_append(&vs->output, data, len); @@ -3022,18 +3022,16 @@ static void vnc_connect(VncDisplay *vd, int csock, vs->websocket = 1; #ifdef CONFIG_VNC_TLS if (vd->ws_tls) { - qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_io, - NULL, vs); + qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs); } else #endif /* CONFIG_VNC_TLS */ { - qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, - NULL, vs); + qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); } } else #endif /* CONFIG_VNC_WS */ { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); } vnc_client_cache_addr(vs); @@ -3182,14 +3180,14 @@ static void vnc_display_close(VncDisplay *vs) vs->enabled = false; vs->is_unix = false; if (vs->lsock != -1) { - qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(vs->lsock, NULL, NULL, NULL); close(vs->lsock); vs->lsock = -1; } #ifdef CONFIG_VNC_WS vs->ws_enabled = false; if (vs->lwebsock != -1) { - qemu_set_fd_handler2(vs->lwebsock, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL); close(vs->lwebsock); vs->lwebsock = -1; } @@ -3707,12 +3705,11 @@ void vnc_display_open(const char *id, Error **errp) #endif /* CONFIG_VNC_WS */ } vs->enabled = true; - qemu_set_fd_handler2(vs->lsock, NULL, - vnc_listen_regular_read, NULL, vs); + qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs); #ifdef CONFIG_VNC_WS if (vs->ws_enabled) { - qemu_set_fd_handler2(vs->lwebsock, NULL, - vnc_listen_websocket_read, NULL, vs); + qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read, + NULL, vs); } #endif /* CONFIG_VNC_WS */ } diff --git a/util/event_notifier-posix.c b/util/event_notifier-posix.c index 8442c6e..ed4ca2b 100644 --- a/util/event_notifier-posix.c +++ b/util/event_notifier-posix.c @@ -85,7 +85,8 @@ int event_notifier_get_fd(EventNotifier *e) int event_notifier_set_handler(EventNotifier *e, EventNotifierHandler *handler) { - return qemu_set_fd_handler(e->rfd, (IOHandler *)handler, NULL, e); + qemu_set_fd_handler(e->rfd, (IOHandler *)handler, NULL, e); + return 0; } int event_notifier_set(EventNotifier *e) diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index f9ad34e..4026314 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -244,7 +244,7 @@ static void wait_for_connect(void *opaque) bool in_progress; Error *err = NULL; - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); do { rc = qemu_getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &valsize); @@ -316,8 +316,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress, if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { connect_state->fd = sock; - qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect, - connect_state); + qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state); *in_progress = true; } else if (rc < 0) { error_setg_errno(errp, errno, "Failed to connect socket"); @@ -796,8 +795,7 @@ int unix_connect_opts(QemuOpts *opts, Error **errp, if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { connect_state->fd = sock; - qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect, - connect_state); + qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state); return sock; } else if (rc >= 0) { /* non blocking socket immediate success, call callback */ diff --git a/util/throttle.c b/util/throttle.c index f976ac7..706c131 100644 --- a/util/throttle.c +++ b/util/throttle.c @@ -1,10 +1,12 @@ /* * QEMU throttling infrastructure * - * Copyright (C) Nodalink, SARL. 2013 + * Copyright (C) Nodalink, EURL. 2013-2014 + * Copyright (C) Igalia, S.L. 2015 * - * Author: - * Benoît Canet <benoit.canet@irqsave.net> + * Authors: + * Benoît Canet <benoit.canet@nodalink.com> + * Alberto Garcia <berto@igalia.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -159,29 +161,36 @@ bool throttle_compute_timer(ThrottleState *ts, } /* Add timers to event loop */ -void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context) +void throttle_timers_attach_aio_context(ThrottleTimers *tt, + AioContext *new_context) { - ts->timers[0] = aio_timer_new(new_context, ts->clock_type, SCALE_NS, - ts->read_timer_cb, ts->timer_opaque); - ts->timers[1] = aio_timer_new(new_context, ts->clock_type, SCALE_NS, - ts->write_timer_cb, ts->timer_opaque); + tt->timers[0] = aio_timer_new(new_context, tt->clock_type, SCALE_NS, + tt->read_timer_cb, tt->timer_opaque); + tt->timers[1] = aio_timer_new(new_context, tt->clock_type, SCALE_NS, + tt->write_timer_cb, tt->timer_opaque); } /* To be called first on the ThrottleState */ -void throttle_init(ThrottleState *ts, - AioContext *aio_context, - QEMUClockType clock_type, - QEMUTimerCB *read_timer_cb, - QEMUTimerCB *write_timer_cb, - void *timer_opaque) +void throttle_init(ThrottleState *ts) { memset(ts, 0, sizeof(ThrottleState)); +} + +/* To be called first on the ThrottleTimers */ +void throttle_timers_init(ThrottleTimers *tt, + AioContext *aio_context, + QEMUClockType clock_type, + QEMUTimerCB *read_timer_cb, + QEMUTimerCB *write_timer_cb, + void *timer_opaque) +{ + memset(tt, 0, sizeof(ThrottleTimers)); - ts->clock_type = clock_type; - ts->read_timer_cb = read_timer_cb; - ts->write_timer_cb = write_timer_cb; - ts->timer_opaque = timer_opaque; - throttle_attach_aio_context(ts, aio_context); + tt->clock_type = clock_type; + tt->read_timer_cb = read_timer_cb; + tt->write_timer_cb = write_timer_cb; + tt->timer_opaque = timer_opaque; + throttle_timers_attach_aio_context(tt, aio_context); } /* destroy a timer */ @@ -195,25 +204,25 @@ static void throttle_timer_destroy(QEMUTimer **timer) } /* Remove timers from event loop */ -void throttle_detach_aio_context(ThrottleState *ts) +void throttle_timers_detach_aio_context(ThrottleTimers *tt) { int i; for (i = 0; i < 2; i++) { - throttle_timer_destroy(&ts->timers[i]); + throttle_timer_destroy(&tt->timers[i]); } } -/* To be called last on the ThrottleState */ -void throttle_destroy(ThrottleState *ts) +/* To be called last on the ThrottleTimers */ +void throttle_timers_destroy(ThrottleTimers *tt) { - throttle_detach_aio_context(ts); + throttle_timers_detach_aio_context(tt); } /* is any throttling timer configured */ -bool throttle_have_timer(ThrottleState *ts) +bool throttle_timers_are_initialized(ThrottleTimers *tt) { - if (ts->timers[0]) { + if (tt->timers[0]) { return true; } @@ -324,9 +333,12 @@ static void throttle_cancel_timer(QEMUTimer *timer) /* Used to configure the throttle * * @ts: the throttle state we are working on + * @tt: the throttle timers we use in this aio context * @cfg: the config to set */ -void throttle_config(ThrottleState *ts, ThrottleConfig *cfg) +void throttle_config(ThrottleState *ts, + ThrottleTimers *tt, + ThrottleConfig *cfg) { int i; @@ -336,10 +348,10 @@ void throttle_config(ThrottleState *ts, ThrottleConfig *cfg) throttle_fix_bucket(&ts->cfg.buckets[i]); } - ts->previous_leak = qemu_clock_get_ns(ts->clock_type); + ts->previous_leak = qemu_clock_get_ns(tt->clock_type); for (i = 0; i < 2; i++) { - throttle_cancel_timer(ts->timers[i]); + throttle_cancel_timer(tt->timers[i]); } } @@ -358,12 +370,15 @@ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg) * * NOTE: this function is not unit tested due to it's usage of timer_mod * + * @tt: the timers structure * @is_write: the type of operation (read/write) * @ret: true if the timer has been scheduled else false */ -bool throttle_schedule_timer(ThrottleState *ts, bool is_write) +bool throttle_schedule_timer(ThrottleState *ts, + ThrottleTimers *tt, + bool is_write) { - int64_t now = qemu_clock_get_ns(ts->clock_type); + int64_t now = qemu_clock_get_ns(tt->clock_type); int64_t next_timestamp; bool must_wait; @@ -378,12 +393,12 @@ bool throttle_schedule_timer(ThrottleState *ts, bool is_write) } /* request throttled and timer pending -> do nothing */ - if (timer_pending(ts->timers[is_write])) { + if (timer_pending(tt->timers[is_write])) { return true; } /* request throttled and timer not pending -> arm timer */ - timer_mod(ts->timers[is_write], next_timestamp); + timer_mod(tt->timers[is_write], next_timestamp); return true; } |